]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Improve directory traversals on POSIX system. Delay changing a working
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Wed, 15 Feb 2012 11:14:48 +0000 (20:14 +0900)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Wed, 15 Feb 2012 11:14:48 +0000 (20:14 +0900)
directory until it's really needed and avoid unnecessary changing
directories.

libarchive/archive_read_disk_entry_from_file.c
libarchive/archive_read_disk_posix.c
libarchive/archive_read_disk_private.h

index 201ea42f51557994b30bd0c70c6be96cc73e5d14..d5d9eaae7592303e657f7d13bd91ab737699e305 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2003-2009 Tim Kientzle
- * Copyright (c) 2010 Michihiro NAKAJIMA
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -115,13 +115,13 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010
 #endif
 
 static int setup_acls_posix1e(struct archive_read_disk *,
-    struct archive_entry *, int fd);
+    struct archive_entry *, int *fd);
 static int setup_mac_metadata(struct archive_read_disk *,
-    struct archive_entry *, int fd);
+    struct archive_entry *, int *fd);
 static int setup_xattrs(struct archive_read_disk *,
-    struct archive_entry *, int fd);
+    struct archive_entry *, int *fd);
 static int setup_sparse(struct archive_read_disk *,
-    struct archive_entry *, int fd);
+    struct archive_entry *, int *fd);
 
 int
 archive_read_disk_entry_from_file(struct archive *_a,
@@ -189,8 +189,13 @@ archive_read_disk_entry_from_file(struct archive *_a,
         * this is an extra step, it has a nice side-effect: We get an
         * open file descriptor which we can use in the subsequent lookups. */
        if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
-               if (fd < 0)
-                       fd = open(path, O_RDONLY | O_NONBLOCK);
+               if (fd < 0) {
+                       if (a->tree != NULL)
+                               fd = a->open_on_current_dir(a->tree, path,
+                                       O_RDONLY | O_NONBLOCK);
+                       else
+                               fd = open(path, O_RDONLY | O_NONBLOCK);
+               }
                if (fd >= 0) {
                        unsigned long stflags;
                        r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
@@ -212,13 +217,21 @@ archive_read_disk_entry_from_file(struct archive *_a,
                            "Couldn't read link data");
                        return (ARCHIVE_FAILED);
                }
+               if (a->tree != NULL) {
 #ifdef HAVE_READLINKAT
-               if (a->entry_wd_fd >= 0)
-                       lnklen = readlinkat(a->entry_wd_fd, path,
-                           linkbuffer, linkbuffer_len);
-               else
+                       lnklen = readlinkat(a->tree_current_dir_fd(a->tree),
+                           path, linkbuffer, linkbuffer_len);
+#else
+                       if (a->tree_enter_working_dir(a->tree) != 0) {
+                               archive_set_error(&a->archive, errno,
+                                   "Couldn't read link data");
+                               free(linkbuffer);
+                               return (ARCHIVE_FAILED);
+                       }
+                       lnklen = readlink(path, linkbuffer, linkbuffer_len);
 #endif /* HAVE_READLINKAT */
-               lnklen = readlink(path, linkbuffer, linkbuffer_len);
+               } else
+                       lnklen = readlink(path, linkbuffer, linkbuffer_len);
                if (lnklen < 0) {
                        archive_set_error(&a->archive, errno,
                            "Couldn't read link data");
@@ -231,16 +244,16 @@ archive_read_disk_entry_from_file(struct archive *_a,
        }
 #endif /* HAVE_READLINK || HAVE_READLINKAT */
 
-       r = setup_acls_posix1e(a, entry, fd);
-       r1 = setup_xattrs(a, entry, fd);
+       r = setup_acls_posix1e(a, entry, &fd);
+       r1 = setup_xattrs(a, entry, &fd);
        if (r1 < r)
                r = r1;
        if (a->enable_copyfile) {
-               r1 = setup_mac_metadata(a, entry, fd);
+               r1 = setup_mac_metadata(a, entry, &fd);
                if (r1 < r)
                        r = r1;
        }
-       r1 = setup_sparse(a, entry, fd);
+       r1 = setup_sparse(a, entry, &fd);
        if (r1 < r)
                r = r1;
 
@@ -266,7 +279,7 @@ archive_read_disk_entry_from_file(struct archive *_a,
  */
 static int
 setup_mac_metadata(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        int tempfd = -1;
        int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;
@@ -276,6 +289,7 @@ setup_mac_metadata(struct archive_read_disk *a,
        int have_attrs;
        const char *name, *tempdir, *tempfile = NULL;
 
+       (void)fd; /* UNUSED */
        name = archive_entry_sourcepath(entry);
        if (name == NULL)
                name = archive_entry_pathname(entry);
@@ -285,6 +299,14 @@ setup_mac_metadata(struct archive_read_disk *a,
                return (ARCHIVE_WARN);
        }
 
+       if (a->tree != NULL) {
+               if (a->tree_enter_working_dir(a->tree) != 0) {
+                       archive_set_error(&a->archive, errno,
+                                   "Couldn't change dir");
+                               return (ARCHIVE_FAILED);
+               }
+       }
+
        /* Short-circuit if there's nothing to do. */
        have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
        if (have_attrs == -1) {
@@ -355,7 +377,7 @@ cleanup:
  */
 static int
 setup_mac_metadata(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        (void)a; /* UNUSED */
        (void)entry; /* UNUSED */
@@ -371,7 +393,7 @@ static void setup_acl_posix1e(struct archive_read_disk *a,
 
 static int
 setup_acls_posix1e(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        const char      *accpath;
        acl_t            acl;
@@ -382,9 +404,20 @@ setup_acls_posix1e(struct archive_read_disk *a,
 
        archive_entry_acl_clear(entry);
 
+       if (*fd < 0 && a->tree != NULL &&
+           (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)){
+               *fd = a->open_on_current_dir(a->tree, accpath,
+                               O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
+                       archive_set_error(&a->archive, errno,
+                           "Couldn't access %s", accpath);
+                       return (ARCHIVE_FAILED);
+               }
+       }
+
        /* Retrieve access ACL from file. */
-       if (fd >= 0)
-               acl = acl_get_fd(fd);
+       if (*fd >= 0)
+               acl = acl_get_fd(*fd);
 #if HAVE_ACL_GET_LINK_NP
        else if (!a->follow_symlinks)
                acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
@@ -478,7 +511,7 @@ setup_acl_posix1e(struct archive_read_disk *a,
 #else
 static int
 setup_acls_posix1e(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        (void)a;      /* UNUSED */
        (void)entry;  /* UNUSED */
@@ -572,7 +605,7 @@ setup_xattr(struct archive_read_disk *a,
 
 static int
 setup_xattrs(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        char *list, *p;
        const char *path;
@@ -582,16 +615,30 @@ setup_xattrs(struct archive_read_disk *a,
        if (path == NULL)
                path = archive_entry_pathname(entry);
 
+       if (*fd < 0 && a->tree != NULL) {
+               if (a->follow_symlinks ||
+                   archive_entry_filetype(entry) != AE_IFLNK)
+                       *fd = a->open_on_current_dir(a->tree, path,
+                               O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
+                       if (a->tree_enter_working_dir(a->tree) != 0) {
+                               archive_set_error(&a->archive, errno,
+                                   "Couldn't access %s", path);
+                               return (ARCHIVE_FAILED);
+                       }
+               }
+       }
+
 #if HAVE_FLISTXATTR
-       if (fd >= 0)
-               list_size = flistxattr(fd, NULL, 0);
+       if (*fd >= 0)
+               list_size = flistxattr(*fd, NULL, 0);
        else if (!a->follow_symlinks)
                list_size = llistxattr(path, NULL, 0);
        else
                list_size = listxattr(path, NULL, 0);
 #elif HAVE_FLISTEA
-       if (fd >= 0)
-               list_size = flistea(fd, NULL, 0);
+       if (*fd >= 0)
+               list_size = flistea(*fd, NULL, 0);
        else if (!a->follow_symlinks)
                list_size = llistea(path, NULL, 0);
        else
@@ -615,15 +662,15 @@ setup_xattrs(struct archive_read_disk *a,
        }
 
 #if HAVE_FLISTXATTR
-       if (fd >= 0)
-               list_size = flistxattr(fd, list, list_size);
+       if (*fd >= 0)
+               list_size = flistxattr(*fd, list, list_size);
        else if (!a->follow_symlinks)
                list_size = llistxattr(path, list, list_size);
        else
                list_size = listxattr(path, list, list_size);
 #elif HAVE_FLISTEA
-       if (fd >= 0)
-               list_size = flistea(fd, list, list_size);
+       if (*fd >= 0)
+               list_size = flistea(*fd, list, list_size);
        else if (!a->follow_symlinks)
                list_size = llistea(path, list, list_size);
        else
@@ -641,7 +688,7 @@ setup_xattrs(struct archive_read_disk *a,
                if (strncmp(p, "system.", 7) == 0 ||
                                strncmp(p, "xfsroot.", 8) == 0)
                        continue;
-               setup_xattr(a, entry, p, fd);
+               setup_xattr(a, entry, p, *fd);
        }
 
        free(list);
@@ -715,7 +762,7 @@ setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
 
 static int
 setup_xattrs(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        char buff[512];
        char *list, *p;
@@ -727,8 +774,22 @@ setup_xattrs(struct archive_read_disk *a,
        if (path == NULL)
                path = archive_entry_pathname(entry);
 
-       if (fd >= 0)
-               list_size = extattr_list_fd(fd, namespace, NULL, 0);
+       if (*fd < 0 && a->tree != NULL) {
+               if (a->follow_symlinks ||
+                   archive_entry_filetype(entry) != AE_IFLNK)
+                       *fd = a->open_on_current_dir(a->tree, path,
+                               O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
+                       if (a->tree_enter_working_dir(a->tree) != 0) {
+                               archive_set_error(&a->archive, errno,
+                                   "Couldn't access %s", path);
+                               return (ARCHIVE_FAILED);
+                       }
+               }
+       }
+
+       if (*fd >= 0)
+               list_size = extattr_list_fd(*fd, namespace, NULL, 0);
        else if (!a->follow_symlinks)
                list_size = extattr_list_link(path, namespace, NULL, 0);
        else
@@ -750,8 +811,8 @@ setup_xattrs(struct archive_read_disk *a,
                return (ARCHIVE_FATAL);
        }
 
-       if (fd >= 0)
-               list_size = extattr_list_fd(fd, namespace, list, list_size);
+       if (*fd >= 0)
+               list_size = extattr_list_fd(*fd, namespace, list, list_size);
        else if (!a->follow_symlinks)
                list_size = extattr_list_link(path, namespace, list, list_size);
        else
@@ -773,7 +834,7 @@ setup_xattrs(struct archive_read_disk *a,
                name = buff + strlen(buff);
                memcpy(name, p + 1, len);
                name[len] = '\0';
-               setup_xattr(a, entry, namespace, name, buff, fd);
+               setup_xattr(a, entry, namespace, name, buff, *fd);
                p += 1 + len;
        }
 
@@ -788,7 +849,7 @@ setup_xattrs(struct archive_read_disk *a,
  */
 static int
 setup_xattrs(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        (void)a;     /* UNUSED */
        (void)entry; /* UNUSED */
@@ -817,14 +878,13 @@ setup_xattrs(struct archive_read_disk *a,
 
 static int
 setup_sparse(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        char buff[4096];
        struct fiemap *fm;
        struct fiemap_extent *fe;
        int64_t size;
        int count, do_fiemap;
-       int initial_fd = fd;
        int exit_sts = ARCHIVE_OK;
 
        if (archive_entry_filetype(entry) != AE_IFREG
@@ -832,14 +892,18 @@ setup_sparse(struct archive_read_disk *a,
            || archive_entry_hardlink(entry) != NULL)
                return (ARCHIVE_OK);
 
-       if (fd < 0) {
+       if (*fd < 0) {
                const char *path;
 
                path = archive_entry_sourcepath(entry);
                if (path == NULL)
                        path = archive_entry_pathname(entry);
-               fd = open(path, O_RDONLY | O_NONBLOCK);
-               if (fd < 0) {
+               if (a->tree != NULL) {
+                       *fd = a->open_on_current_dir(a->tree, path,
+                               O_RDONLY | O_NONBLOCK);
+               else
+                       *fd = open(path, O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
                        archive_set_error(&a->archive, errno,
                            "Can't open `%s'", path);
                        return (ARCHIVE_FAILED);
@@ -857,7 +921,7 @@ setup_sparse(struct archive_read_disk *a,
        for (;;) {
                int i, r;
 
-               r = ioctl(fd, FS_IOC_FIEMAP, fm); 
+               r = ioctl(*fd, FS_IOC_FIEMAP, fm); 
                if (r < 0) {
                        /* When something error happens, it is better we
                         * should return ARCHIVE_OK because an earlier
@@ -893,8 +957,6 @@ setup_sparse(struct archive_read_disk *a,
                        break;
        }
 exit_setup_sparse:
-       if (initial_fd != fd)
-               close(fd);
        return (exit_sts);
 }
 
@@ -906,10 +968,9 @@ exit_setup_sparse:
 
 static int
 setup_sparse(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        int64_t size;
-       int initial_fd = fd;
        off_t initial_off; /* FreeBSD/Solaris only, so off_t okay here */
        off_t off_s, off_e; /* FreeBSD/Solaris only, so off_t okay here */
        int exit_sts = ARCHIVE_OK;
@@ -920,22 +981,45 @@ setup_sparse(struct archive_read_disk *a,
                return (ARCHIVE_OK);
 
        /* Does filesystem support the reporting of hole ? */
-       if (fd >= 0) {
-               if (fpathconf(fd, _PC_MIN_HOLE_SIZE) <= 0)
+       if (*fd < 0 && a->tree != NULL) {
+               const char *path;
+
+               path = archive_entry_sourcepath(entry);
+               if (path == NULL)
+                       path = archive_entry_pathname(entry);
+               *fd = a->open_on_current_dir(a->tree, path,
+                               O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
+                       archive_set_error(&a->archive, errno,
+                           "Can't open `%s'", path);
+                       return (ARCHIVE_FAILED);
+               }
+       }
+
+       if (*fd >= 0) {
+               if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0)
                        return (ARCHIVE_OK);
-               initial_off = lseek(fd, 0, SEEK_CUR);
+               initial_off = lseek(*fd, 0, SEEK_CUR);
                if (initial_off != 0)
-                       lseek(fd, 0, SEEK_SET);
+                       lseek(*fd, 0, SEEK_SET);
        } else {
                const char *path;
 
+               if (a->tree != NULL) {
+                       if (a->tree_enter_working_dir(a->tree) != 0) {
+                               archive_set_error(&a->archive, errno,
+                                   "Couldn't change dir");
+                               return (ARCHIVE_FAILED);
+                       }
+               }
                path = archive_entry_sourcepath(entry);
                if (path == NULL)
                        path = archive_entry_pathname(entry);
+                       
                if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
                        return (ARCHIVE_OK);
-               fd = open(path, O_RDONLY | O_NONBLOCK);
-               if (fd < 0) {
+               *fd = open(path, O_RDONLY | O_NONBLOCK);
+               if (*fd < 0) {
                        archive_set_error(&a->archive, errno,
                            "Can't open `%s'", path);
                        return (ARCHIVE_FAILED);
@@ -946,7 +1030,7 @@ setup_sparse(struct archive_read_disk *a,
        off_s = 0;
        size = archive_entry_size(entry);
        while (off_s < size) {
-               off_s = lseek(fd, off_s, SEEK_DATA);
+               off_s = lseek(*fd, off_s, SEEK_DATA);
                if (off_s == (off_t)-1) {
                        if (errno == ENXIO)
                                break;/* no more hole */
@@ -955,10 +1039,10 @@ setup_sparse(struct archive_read_disk *a,
                        exit_sts = ARCHIVE_FAILED;
                        goto exit_setup_sparse;
                }
-               off_e = lseek(fd, off_s, SEEK_HOLE);
+               off_e = lseek(*fd, off_s, SEEK_HOLE);
                if (off_s == (off_t)-1) {
                        if (errno == ENXIO) {
-                               off_e = lseek(fd, 0, SEEK_END);
+                               off_e = lseek(*fd, 0, SEEK_END);
                                if (off_e != (off_t)-1)
                                        break;/* no more data */
                        }
@@ -974,10 +1058,7 @@ setup_sparse(struct archive_read_disk *a,
                off_s = off_e;
        }
 exit_setup_sparse:
-       if (initial_fd != fd)
-               close(fd);
-       else
-               lseek(fd, initial_off, SEEK_SET);
+       lseek(*fd, initial_off, SEEK_SET);
        return (exit_sts);
 }
 
@@ -988,7 +1069,7 @@ exit_setup_sparse:
  */
 static int
 setup_sparse(struct archive_read_disk *a,
-    struct archive_entry *entry, int fd)
+    struct archive_entry *entry, int *fd)
 {
        (void)a;     /* UNUSED */
        (void)entry; /* UNUSED */
index fa2a0a403a326bcfebc7f67debe510a1a17777cf..cebb305452c1cd14eddabd322dae6b126c3b5d36 100644 (file)
@@ -257,6 +257,7 @@ struct tree {
 #define        onWorkingDir    64 /* We are on the working dir where we are
                            * reading directory entry at this time. */
 #define        needsRestoreTimes 128
+#define        onInitialDir    256 /* We are on the initial dir. */
 
 static int
 tree_dir_next_posix(struct tree *t);
@@ -359,6 +360,7 @@ static const char *trivial_lookup_uname(void *, int64_t uid);
 static int     setup_sparse(struct archive_read_disk *, struct archive_entry *);
 static int     close_and_restore_time(int fd, struct tree *,
                    struct restore_time *);
+static int     open_on_current_dir(struct tree *, const char *, int);
 
 
 static struct archive_vtable *
@@ -457,7 +459,9 @@ archive_read_disk_new(void)
        a->lookup_gname = trivial_lookup_gname;
        a->enable_copyfile = 1;
        a->traverse_mount_points = 1;
-       a->entry_wd_fd = -1;
+       a->open_on_current_dir = open_on_current_dir;
+       a->tree_current_dir_fd = tree_current_dir_fd;
+       a->tree_enter_working_dir = tree_enter_working_dir;
        return (&a->archive);
 }
 
@@ -734,13 +738,8 @@ _archive_read_data_block(struct archive *_a, const void **buff,
                        flags |= O_NOATIME;
                do {
 #endif
-#ifdef HAVE_OPENAT
-                       t->entry_fd = openat(tree_current_dir_fd(t),
+                       t->entry_fd = open_on_current_dir(t,
                            tree_current_access_path(t), flags);
-#else
-                       tree_enter_working_dir(t);
-                       t->entry_fd = open(tree_current_access_path(t), flags);
-#endif
 #if defined(O_NOATIME)
                        /*
                         * When we did open the file with O_NOATIME flag,
@@ -869,10 +868,6 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
                close_and_restore_time(t->entry_fd, t, &t->restore_time);
                t->entry_fd = -1;
        }
-#if !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR))
-       /* Restore working directory. */
-       tree_enter_working_dir(t);
-#endif
 
 next_entry:
        st = NULL;
@@ -1007,15 +1002,8 @@ next_entry:
                if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) {
                        unsigned long stflags;
 
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
-                       fd = openat(tree_current_dir_fd(t),
+                       fd = open_on_current_dir(t,
                            tree_current_access_path(t), O_RDONLY | O_NONBLOCK);
-#else
-                       tree_enter_working_dir(t);
-                       fd = open(tree_current_access_path(t),
-                           O_RDONLY | O_NONBLOCK);
-                       
-#endif
                        if (fd >= 0) {
                                r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
                                if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0) {
@@ -1096,26 +1084,6 @@ next_entry:
                }
        }
 
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
-       /*
-        * Open the current file to freely gather its metadata anywhere in
-        * working directory.
-        * Note: A symbolic link file cannot be opened with O_NOFOLLOW.
-        */
-       if (fd < 0 && archive_entry_filetype(entry) != AE_IFLNK)
-               fd = openat(tree_current_dir_fd(t), tree_current_access_path(t),
-                   O_RDONLY | O_NONBLOCK);
-       /* Restore working directory if openat() operation failed or
-        * the file is a symbolic link. */
-       if (fd < 0)
-               tree_enter_working_dir(t);
-
-       /* The current direcotry fd is needed at
-        * archive_read_disk_entry_from_file() function to read link data
-        * with readlinkat(). */
-       a->entry_wd_fd = tree_current_dir_fd(t);
-#endif
-
        /*
         * Populate the archive_entry with metadata from the disk.
         */
@@ -1499,7 +1467,7 @@ setup_current_filesystem(struct archive_read_disk *a)
        t->current_filesystem->synthetic = -1;
        t->current_filesystem->remote = -1;
        if (tree_current_is_symblic_link_target(t)) {
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
+#if defined(HAVE_OPENAT)
                /*
                 * Get file system statistics on any directory
                 * where current is.
@@ -1516,6 +1484,10 @@ setup_current_filesystem(struct archive_read_disk *a)
                        xr = get_xfer_size(t, fd, NULL);
                close(fd);
 #else
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                r = statfs(tree_current_access_path(t), &sfs);
                if (r == 0)
                        xr = get_xfer_size(t, -1, tree_current_access_path(t));
@@ -1665,7 +1637,7 @@ setup_current_filesystem(struct archive_read_disk *a)
        int r, vr = 0, xr = 0;
 
        if (tree_current_is_symblic_link_target(t)) {
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
+#if defined(HAVE_OPENAT)
                /*
                 * Get file system statistics on any directory
                 * where current is.
@@ -1683,6 +1655,10 @@ setup_current_filesystem(struct archive_read_disk *a)
                        xr = get_xfer_size(t, fd, NULL);
                close(fd);
 #else
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                vr = statvfs(tree_current_access_path(t), &svfs);
                r = statfs(tree_current_access_path(t), &sfs);
                if (r == 0)
@@ -1694,9 +1670,11 @@ setup_current_filesystem(struct archive_read_disk *a)
                r = fstatfs(tree_current_dir_fd(t), &sfs);
                if (r == 0)
                        xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
-#elif defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
-#error "Unexpected case. Please tell us about this error."
 #else
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                vr = statvfs(".", &svfs);
                r = statfs(".", &sfs);
                if (r == 0)
@@ -1767,7 +1745,7 @@ setup_current_filesystem(struct archive_read_disk *a)
        t->current_filesystem->synthetic = -1;/* Not supported */
        t->current_filesystem->remote = -1;/* Not supported */
        if (tree_current_is_symblic_link_target(t)) {
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
+#if defined(HAVE_OPENAT)
                /*
                 * Get file system statistics on any directory
                 * where current is.
@@ -1784,6 +1762,10 @@ setup_current_filesystem(struct archive_read_disk *a)
                        xr = get_xfer_size(t, fd, NULL);
                close(fd);
 #else
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                r = statvfs(tree_current_access_path(t), &sfs);
                if (r == 0)
                        xr = get_xfer_size(t, -1, tree_current_access_path(t));
@@ -1793,9 +1775,11 @@ setup_current_filesystem(struct archive_read_disk *a)
                r = fstatvfs(tree_current_dir_fd(t), &sfs);
                if (r == 0)
                        xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
-#elif defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
-#error "Unexpected case. Please tell us about this error."
 #else
+               if (tree_enter_working_dir(t) != 0) {
+                       archive_set_error(&a->archive, errno, "fchdir failed");
+                       return (ARCHIVE_FAILED);
+               }
                r = statvfs(".", &sfs);
                if (r == 0)
                        xr = get_xfer_size(t, -1, ".");
@@ -1935,6 +1919,18 @@ close_and_restore_time(int fd, struct tree *t, struct restore_time *rt)
        return (0);
 }
 
+static int
+open_on_current_dir(struct tree *t, const char *path, int flags)
+{
+#ifdef HAVE_OPENAT
+       return (openat(tree_current_dir_fd(t), path, flags));
+#else
+       if (tree_enter_working_dir(t) != 0)
+               return (-1);
+       return (open(path, flags));
+#endif
+}
+
 /*
  * Add a directory path to the current stack.
  */
@@ -2016,6 +2012,7 @@ static struct tree *
 tree_reopen(struct tree *t, const char *path, int restore_time)
 {
        t->flags = (restore_time)?needsRestoreTimes:0;
+       t->flags |= onInitialDir;
        t->visit_type = 0;
        t->tree_errno = 0;
        t->dirname_length = 0;
@@ -2049,17 +2046,7 @@ tree_descent(struct tree *t)
 #if defined(O_DIRECTORY)
        flag |= O_DIRECTORY;
 #endif
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
-       new_fd = openat(t->working_dir_fd, t->stack->name.s, flag);
-#else
-       new_fd = open(t->stack->name.s, flag);
-       if (new_fd >= 0) {
-               if (fchdir(new_fd) != 0) {
-                       close(new_fd);
-                       new_fd = -1;
-               }
-       }
-#endif
+       new_fd = open_on_current_dir(t, t->stack->name.s, flag);
        if (new_fd < 0) {
                t->tree_errno = errno;
                r = TREE_ERROR_DIR;
@@ -2073,7 +2060,9 @@ tree_descent(struct tree *t)
                                t->maxOpenCount = t->openCount;
                } else
                        close(t->working_dir_fd);
+               /* Renew the current working directory. */
                t->working_dir_fd = new_fd;
+               t->flags &= ~onWorkingDir;
        }
        return (r);
 }
@@ -2089,29 +2078,17 @@ tree_ascend(struct tree *t)
 
        te = t->stack;
        prev_dir_fd = t->working_dir_fd;
-#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR)
        if (te->flags & isDirLink)
                new_fd = te->symlink_parent_fd;
        else
-               new_fd = openat(t->working_dir_fd, "..", O_RDONLY);
-#else
-       if (te->flags & isDirLink)
-               new_fd = te->symlink_parent_fd;
-       else
-               new_fd = open("..", O_RDONLY);
-       if (new_fd >= 0) {
-               if (fchdir(new_fd) != 0) {
-                       if ((te->flags & isDirLink) == 0)
-                               close(new_fd);
-                       new_fd = -1;
-               }
-       }
-#endif
+               new_fd = open_on_current_dir(t, "..", O_RDONLY);
        if (new_fd < 0) {
                t->tree_errno = errno;
                r = TREE_ERROR_FATAL;
        } else {
+               /* Renew the current working directory. */
                t->working_dir_fd = new_fd;
+               t->flags &= ~onWorkingDir;
                /* Current directory has been changed, we should
                 * close an fd of previous working directory. */
                close_and_restore_time(prev_dir_fd, t, &te->restore_time);
@@ -2132,10 +2109,12 @@ tree_enter_initial_dir(struct tree *t)
 {
        int r = 0;
 
-       if (t->flags & onWorkingDir) {
+       if ((t->flags & onInitialDir) == 0) {
                r = fchdir(t->initial_dir_fd);
-               if (r == 0)
+               if (r == 0) {
                        t->flags &= ~onWorkingDir;
+                       t->flags |= onInitialDir;
+               }
        }
        return (r);
 }
@@ -2155,8 +2134,10 @@ tree_enter_working_dir(struct tree *t)
         */
        if (t->depth > 0 && (t->flags & onWorkingDir) == 0) {
                r = fchdir(t->working_dir_fd);
-               if (r == 0)
+               if (r == 0) {
+                       t->flags &= ~onInitialDir;
                        t->flags |= onWorkingDir;
+               }
        }
        return (r);
 }
@@ -2265,7 +2246,8 @@ tree_dir_next_posix(struct tree *t)
 #if defined(HAVE_FDOPENDIR)
                if ((t->d = fdopendir(dup(t->working_dir_fd))) == NULL) {
 #else
-               if ((t->d = opendir(".")) == NULL) {
+               if (tree_enter_working_dir(t) != 0 ||
+                   (t->d = opendir(".")) == NULL) {
 #endif
                        r = tree_ascend(t); /* Undo "chdir" */
                        tree_pop(t);
@@ -2336,6 +2318,8 @@ tree_current_stat(struct tree *t)
                if (fstatat(tree_current_dir_fd(t),
                    tree_current_access_path(t), &t->st, 0) != 0)
 #else
+               if (tree_enter_working_dir(t) != 0)
+                       return NULL;
                if (stat(tree_current_access_path(t), &t->st) != 0)
 #endif
                        return NULL;
@@ -2356,6 +2340,8 @@ tree_current_lstat(struct tree *t)
                    tree_current_access_path(t), &t->lst,
                    AT_SYMLINK_NOFOLLOW) != 0)
 #else
+               if (tree_enter_working_dir(t) != 0)
+                       return NULL;
                if (lstat(tree_current_access_path(t), &t->lst) != 0)
 #endif
                        return NULL;
@@ -2377,7 +2363,10 @@ tree_current_is_dir(struct tree *t)
         */
        if (t->flags & hasLstat) {
                /* If lstat() says it's a dir, it must be a dir. */
-               if (S_ISDIR(tree_current_lstat(t)->st_mode))
+               st = tree_current_lstat(t);
+               if (st == NULL)
+                       return 0;
+               if (S_ISDIR(st->st_mode))
                        return 1;
                /* Not a dir; might be a link to a dir. */
                /* If it's not a link, then it's not a link to a dir. */
@@ -2411,9 +2400,13 @@ tree_current_is_physical_dir(struct tree *t)
         * If stat() says it isn't a dir, then it's not a dir.
         * If stat() data is cached, this check is free, so do it first.
         */
-       if ((t->flags & hasStat)
-           && (!S_ISDIR(tree_current_stat(t)->st_mode)))
-               return 0;
+       if (t->flags & hasStat) {
+               st = tree_current_stat(t);
+               if (st == NULL)
+                       return (0);
+               if (!S_ISDIR(st->st_mode))
+                       return (0);
+       }
 
        /*
         * Either stat() said it was a dir (in which case, we have
@@ -2457,7 +2450,7 @@ tree_current_is_symblic_link_target(struct tree *t)
 
        lst = tree_current_lstat(t);
        st = tree_current_stat(t);
-       return (st != NULL &&
+       return (st != NULL && lst != NULL &&
            (int64_t)st->st_dev == t->current_filesystem->dev &&
            st->st_dev != lst->st_dev);
 }
index fa73a2410b9b261675345562453058980621ffe9..e5af16b9161ba10319865d46ced901fc47d294ed 100644 (file)
@@ -56,6 +56,9 @@ struct archive_read_disk {
 
        /* Directory traversals. */
        struct tree *tree;
+       int     (*open_on_current_dir)(struct tree*, const char *, int);
+       int     (*tree_current_dir_fd)(struct tree*);
+       int     (*tree_enter_working_dir)(struct tree*);
 
        /* Set 1 if users request to restore atime . */
        int              restore_time;
@@ -66,8 +69,6 @@ struct archive_read_disk {
        /* Set 1 if users request to traverse mount points. */
        int              traverse_mount_points;
 
-       int              entry_wd_fd;
-
        const char * (*lookup_gname)(void *private, int64_t gid);
        void    (*cleanup_gname)(void *private);
        void     *lookup_gname_data;