From: Michihiro NAKAJIMA Date: Wed, 15 Feb 2012 11:14:48 +0000 (+0900) Subject: Improve directory traversals on POSIX system. Delay changing a working X-Git-Tag: v3.0.4~2^2~89 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3439b41d9252372753dc8ca2c7b3e6c0dcfc2874;p=thirdparty%2Flibarchive.git Improve directory traversals on POSIX system. Delay changing a working directory until it's really needed and avoid unnecessary changing directories. --- diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c index 201ea42f5..d5d9eaae7 100644 --- a/libarchive/archive_read_disk_entry_from_file.c +++ b/libarchive/archive_read_disk_entry_from_file.c @@ -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 */ diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c index fa2a0a403..cebb30545 100644 --- a/libarchive/archive_read_disk_posix.c +++ b/libarchive/archive_read_disk_posix.c @@ -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); } diff --git a/libarchive/archive_read_disk_private.h b/libarchive/archive_read_disk_private.h index fa73a2410..e5af16b91 100644 --- a/libarchive/archive_read_disk_private.h +++ b/libarchive/archive_read_disk_private.h @@ -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;