X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/blobdiff_plain/34fcef5eefe61d49e2dcf7b9fd60151dc932823c..f36d037f7f24e424b06fa504925944282ba83bd8:/contrib/libarchive/libarchive/archive_read_disk_posix.c diff --git a/contrib/libarchive/libarchive/archive_read_disk_posix.c b/contrib/libarchive/libarchive/archive_read_disk_posix.c index d7c7aa34b9..02a286944d 100644 --- a/contrib/libarchive/libarchive/archive_read_disk_posix.c +++ b/contrib/libarchive/libarchive/archive_read_disk_posix.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle - * Copyright (c) 2010,2011 Michihiro NAKAJIMA + * Copyright (c) 2010-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -52,6 +52,19 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_LINUX_MAGIC_H #include #endif +#ifdef HAVE_LINUX_FS_H +#include +#endif +/* + * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. + * As the include guards don't agree, the order of include is important. + */ +#ifdef HAVE_LINUX_EXT2_FS_H +#include /* for Linux file flags */ +#endif +#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) +#include /* Linux file flags, broken on Cygwin */ +#endif #ifdef HAVE_DIRECT_H #include #endif @@ -76,6 +89,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif #include "archive.h" #include "archive_string.h" @@ -222,6 +238,7 @@ struct tree { char symlink_mode; struct filesystem *current_filesystem; struct filesystem *filesystem_table; + int initial_filesystem_id; int current_filesystem_id; int max_filesystem_id; int allocated_filesytem; @@ -240,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); @@ -342,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 * @@ -430,16 +449,19 @@ archive_read_disk_new(void) { struct archive_read_disk *a; - a = (struct archive_read_disk *)malloc(sizeof(*a)); + a = (struct archive_read_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); - memset(a, 0, sizeof(*a)); a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_read_disk_vtable(); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; - a->entry_wd_fd = -1; + a->enable_copyfile = 1; + a->traverse_mount_points = 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); } @@ -555,6 +577,37 @@ archive_read_disk_set_atime_restored(struct archive *_a) #endif } +int +archive_read_disk_set_behavior(struct archive *_a, int flags) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + int r = ARCHIVE_OK; + + archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, + ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump"); + + if (flags & ARCHIVE_READDISK_RESTORE_ATIME) + r = archive_read_disk_set_atime_restored(_a); + else { + a->restore_time = 0; + if (a->tree != NULL) + a->tree->flags &= ~needsRestoreTimes; + } + if (flags & ARCHIVE_READDISK_HONOR_NODUMP) + a->honor_nodump = 1; + else + a->honor_nodump = 0; + if (flags & ARCHIVE_READDISK_MAC_COPYFILE) + a->enable_copyfile = 1; + else + a->enable_copyfile = 0; + if (flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) + a->traverse_mount_points = 0; + else + a->traverse_mount_points = 1; + return (r); +} + /* * Trivial implementations of gname/uname lookup functions. * These are normally overridden by the client, but these stub @@ -685,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, @@ -733,7 +781,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, t->entry_buff_size = t->current_filesystem->buff_size; buffbytes = t->entry_buff_size; - if (buffbytes > t->current_sparse->length) + if ((int64_t)buffbytes > t->current_sparse->length) buffbytes = t->current_sparse->length; /* @@ -802,29 +850,17 @@ abort_read_data: } static int -_archive_read_next_header2(struct archive *_a, struct archive_entry *entry) +next_entry(struct archive_read_disk *a, struct tree *t, + struct archive_entry *entry) { - struct archive_read_disk *a = (struct archive_read_disk *)_a; - struct tree *t; const struct stat *st; /* info to use for this entry */ const struct stat *lst;/* lstat() information */ - int descend, fd = -1, r; - - archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, - ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, - "archive_read_next_header2"); + const char *name; + int descend, r; - t = a->tree; - if (t->entry_fd >= 0) { - 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 st = NULL; lst = NULL; + t->descend = 0; do { switch (tree_next(t)) { case TREE_ERROR_FATAL: @@ -859,6 +895,38 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) } } while (lst == NULL); +#ifdef __APPLE__ + if (a->enable_copyfile) { + /* If we're using copyfile(), ignore "._XXX" files. */ + const char *bname = strrchr(tree_current_path(t), '/'); + if (bname == NULL) + bname = tree_current_path(t); + else + ++bname; + if (bname[0] == '.' && bname[1] == '_') + return (ARCHIVE_RETRY); + } +#endif + + archive_entry_copy_pathname(entry, tree_current_path(t)); + /* + * Perform path matching. + */ + if (a->matching) { + r = archive_match_path_excluded(a->matching, entry); + if (r < 0) { + archive_set_error(&(a->archive), errno, + "Faild : %s", archive_error_string(a->matching)); + return (r); + } + if (r) { + if (a->excluded_cb_func) + a->excluded_cb_func(&(a->archive), + a->excluded_cb_data, entry); + return (ARCHIVE_RETRY); + } + } + /* * Distinguish 'L'/'P'/'H' symlink following. */ @@ -897,13 +965,44 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) tree_enter_initial_dir(t); return (ARCHIVE_FATAL); } + if (t->initial_filesystem_id == -1) + t->initial_filesystem_id = t->current_filesystem_id; + if (!a->traverse_mount_points) { + if (t->initial_filesystem_id != t->current_filesystem_id) + return (ARCHIVE_RETRY); + } t->descend = descend; - archive_entry_set_pathname(entry, tree_current_path(t)); - archive_entry_copy_sourcepath(entry, tree_current_access_path(t)); + /* + * Honor nodump flag. + * If the file is marked with nodump flag, do not return this entry. + */ + if (a->honor_nodump) { +#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) + if (st->st_flags & UF_NODUMP) + return (ARCHIVE_RETRY); +#elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) &&\ + defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) + if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) { + unsigned long stflags; + + t->entry_fd = open_on_current_dir(t, + tree_current_access_path(t), O_RDONLY | O_NONBLOCK); + if (t->entry_fd >= 0) { + r = ioctl(t->entry_fd, EXT2_IOC_GETFLAGS, + &stflags); + if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0) + return (ARCHIVE_RETRY); + } + } +#endif + } + archive_entry_copy_stat(entry, st); - /* Save the times to be restored. */ + /* Save the times to be restored. This must be in before + * calling archive_read_disk_descend() or any chance of it, + * especially, invokng a callback. */ t->restore_time.mtime = archive_entry_mtime(entry); t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry); t->restore_time.atime = archive_entry_atime(entry); @@ -911,39 +1010,102 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) t->restore_time.filetype = archive_entry_filetype(entry); t->restore_time.noatime = t->current_filesystem->noatime; -#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. + * Perform time matching. */ - if (a->follow_symlinks || 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); + if (a->matching) { + r = archive_match_time_excluded(a->matching, entry); + if (r < 0) { + archive_set_error(&(a->archive), errno, + "Faild : %s", archive_error_string(a->matching)); + return (r); + } + if (r) { + if (a->excluded_cb_func) + a->excluded_cb_func(&(a->archive), + a->excluded_cb_data, entry); + return (ARCHIVE_RETRY); + } + } - /* 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 + /* Lookup uname/gname */ + name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry)); + if (name != NULL) + archive_entry_copy_uname(entry, name); + name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry)); + if (name != NULL) + archive_entry_copy_gname(entry, name); + + /* + * Perform owner matching. + */ + if (a->matching) { + r = archive_match_owner_excluded(a->matching, entry); + if (r < 0) { + archive_set_error(&(a->archive), errno, + "Faild : %s", archive_error_string(a->matching)); + return (r); + } + if (r) { + if (a->excluded_cb_func) + a->excluded_cb_func(&(a->archive), + a->excluded_cb_data, entry); + return (ARCHIVE_RETRY); + } + } + + /* + * Invoke a meta data filter callback. + */ + if (a->metadata_filter_func) { + if (!a->metadata_filter_func(&(a->archive), + a->metadata_filter_data, entry)) + return (ARCHIVE_RETRY); + } /* * Populate the archive_entry with metadata from the disk. */ - r = archive_read_disk_entry_from_file(&(a->archive), entry, fd, st); + archive_entry_copy_sourcepath(entry, tree_current_access_path(t)); + r = archive_read_disk_entry_from_file(&(a->archive), entry, + t->entry_fd, st); - /* Close the file descriptor used for reding the current file - * metadata at archive_read_disk_entry_from_file(). */ - if (fd >= 0) - close(fd); + return (r); +} + +static int +_archive_read_next_header2(struct archive *_a, struct archive_entry *entry) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + struct tree *t; + int r; + + archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, + "archive_read_next_header2"); + + t = a->tree; + if (t->entry_fd >= 0) { + close_and_restore_time(t->entry_fd, t, &t->restore_time); + t->entry_fd = -1; + } + + for (;;) { + r = next_entry(a, t, entry); + if (t->entry_fd >= 0) { + close(t->entry_fd); + t->entry_fd = -1; + } + + if (r == ARCHIVE_RETRY) { + archive_entry_clear(entry); + continue; + } + break; + } /* Return to the initial directory. */ tree_enter_initial_dir(t); - archive_entry_copy_sourcepath(entry, tree_current_path(t)); /* * EOF and FATAL are persistent at this layer. By @@ -956,6 +1118,8 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) break; case ARCHIVE_OK: case ARCHIVE_WARN: + /* Overwrite the sourcepath based on the initial directory. */ + archive_entry_copy_sourcepath(entry, tree_current_path(t)); t->entry_total = 0; if (archive_entry_filetype(entry) == AE_IFREG) { t->nlink = archive_entry_nlink(entry); @@ -1018,6 +1182,48 @@ setup_sparse(struct archive_read_disk *a, struct archive_entry *entry) return (ARCHIVE_OK); } +int +archive_read_disk_set_matching(struct archive *_a, struct archive *_ma, + void (*_excluded_func)(struct archive *, void *, struct archive_entry *), + void *_client_data) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, + ARCHIVE_STATE_ANY, "archive_read_disk_set_matching"); + a->matching = _ma; + a->excluded_cb_func = _excluded_func; + a->excluded_cb_data = _client_data; + return (ARCHIVE_OK); +} + +int +archive_read_disk_set_metadata_filter_callback(struct archive *_a, + int (*_metadata_filter_func)(struct archive *, void *, + struct archive_entry *), void *_client_data) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + + archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, + "archive_read_disk_set_metadata_filter_callback"); + + a->metadata_filter_func = _metadata_filter_func; + a->metadata_filter_data = _client_data; + return (ARCHIVE_OK); +} + +int +archive_read_disk_can_descend(struct archive *_a) +{ + struct archive_read_disk *a = (struct archive_read_disk *)_a; + struct tree *t = a->tree; + + archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, + "archive_read_disk_can_descend"); + + return (t->visit_type == TREE_REGULAR && t->descend); +} + /* * Called by the client to mark the directory just returned from * tree_next() as needing to be visited. @@ -1028,14 +1234,12 @@ archive_read_disk_descend(struct archive *_a) struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; - archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, + archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_descend"); - if (t->visit_type != TREE_REGULAR || !t->descend) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Ignored the request descending the current object"); - return (ARCHIVE_WARN); - } + if (t->visit_type != TREE_REGULAR || !t->descend) + return (ARCHIVE_OK); if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, @@ -1079,8 +1283,12 @@ archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) archive_string_init(&path); if (archive_string_append_from_wcs(&path, pathname, wcslen(pathname)) != 0) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Can't convert a path to a char string"); + if (errno == ENOMEM) + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + else + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Can't convert a path to a char string"); a->archive.state = ARCHIVE_STATE_FATAL; ret = ARCHIVE_FATAL; } else @@ -1268,7 +1476,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. @@ -1285,6 +1493,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)); @@ -1334,9 +1546,13 @@ setup_current_filesystem(struct archive_read_disk *a) t->current_filesystem->name_max = sfs.f_namemax; #else /* Mac OS X does not have f_namemax in struct statfs. */ - if (tree_current_is_symblic_link_target(t)) + if (tree_current_is_symblic_link_target(t)) { + if (tree_enter_working_dir(t) != 0) { + archive_set_error(&a->archive, errno, "fchdir failed"); + return (ARCHIVE_FAILED); + } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); - else + } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); if (nm == -1) t->current_filesystem->name_max = NAME_MAX; @@ -1360,6 +1576,10 @@ setup_current_filesystem(struct archive_read_disk *a) int r, xr = 0; t->current_filesystem->synthetic = -1; + if (tree_enter_working_dir(t) != 0) { + archive_set_error(&a->archive, errno, "fchdir failed"); + return (ARCHIVE_FAILED); + } if (tree_current_is_symblic_link_target(t)) { r = statvfs(tree_current_access_path(t), &sfs); if (r == 0) @@ -1384,17 +1604,24 @@ setup_current_filesystem(struct archive_read_disk *a) * for pathconf() function. */ t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; +#if defined(HAVE_STRUCT_STATVFS_F_IOSIZE) t->current_filesystem->min_xfer_size = sfs.f_iosize; t->current_filesystem->incr_xfer_size = sfs.f_iosize; +#else + t->current_filesystem->min_xfer_size = sfs.f_bsize; + t->current_filesystem->incr_xfer_size = sfs.f_bsize; +#endif } if (sfs.f_flag & ST_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; +#if defined(ST_NOATIME) if (sfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else +#endif t->current_filesystem->noatime = 0; /* Set maximum filename length. */ @@ -1427,7 +1654,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. @@ -1445,6 +1672,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) @@ -1456,9 +1687,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) @@ -1529,7 +1762,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. @@ -1546,6 +1779,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)); @@ -1555,9 +1792,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, "."); @@ -1615,9 +1854,13 @@ setup_current_filesystem(struct archive_read_disk *a) #if defined(HAVE_READDIR_R) /* Set maximum filename length. */ # if defined(_PC_NAME_MAX) - if (tree_current_is_symblic_link_target(t)) + if (tree_current_is_symblic_link_target(t)) { + if (tree_enter_working_dir(t) != 0) { + archive_set_error(&a->archive, errno, "fchdir failed"); + return (ARCHIVE_FAILED); + } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); - else + } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); if (nm == -1) # endif /* _PC_NAME_MAX */ @@ -1697,6 +1940,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. */ @@ -1778,6 +2033,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; @@ -1790,6 +2046,7 @@ tree_reopen(struct tree *t, const char *path, int restore_time) t->entry_fd = -1; t->entry_eof = 0; t->entry_remaining_bytes = 0; + t->initial_filesystem_id = -1; /* First item is set up a lot like a symlink traversal. */ tree_push(t, path, 0, 0, 0, NULL); @@ -1803,12 +2060,14 @@ tree_reopen(struct tree *t, const char *path, int restore_time) static int tree_descent(struct tree *t) { - int r = 0; + int flag, new_fd, r = 0; -#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR) - int new_fd; t->dirname_length = archive_strlen(&t->path); - new_fd = openat(t->working_dir_fd, t->stack->name.s, O_RDONLY); + flag = O_RDONLY; +#if defined(O_DIRECTORY) + flag |= O_DIRECTORY; +#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; @@ -1822,30 +2081,10 @@ 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; } -#else - /* If it is a link, set up fd for the ascent. */ - if (t->stack->flags & isDirLink) - t->stack->symlink_parent_fd = t->working_dir_fd; - else { - close(t->working_dir_fd); - t->openCount--; - } - t->working_dir_fd = -1; - t->dirname_length = archive_strlen(&t->path); - if (chdir(t->stack->name.s) != 0) - { - t->tree_errno = errno; - r = TREE_ERROR_DIR; - } else { - t->depth++; - t->working_dir_fd = open(".", O_RDONLY); - t->openCount++; - if (t->openCount > t->maxOpenCount) - t->maxOpenCount = t->openCount; - } -#endif return (r); } @@ -1856,37 +2095,21 @@ static int tree_ascend(struct tree *t) { struct tree_entry *te; - int r = 0, prev_dir_fd; + int new_fd, r = 0, prev_dir_fd; te = t->stack; prev_dir_fd = t->working_dir_fd; -#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_FDOPENDIR) if (te->flags & isDirLink) - t->working_dir_fd = te->symlink_parent_fd; - else { - int new_fd = openat(t->working_dir_fd, "..", O_RDONLY); - if (new_fd < 0) { - t->tree_errno = errno; - r = TREE_ERROR_FATAL; - } else - t->working_dir_fd = new_fd; - } -#else - if (te->flags & isDirLink) { - if (fchdir(te->symlink_parent_fd) != 0) { - t->tree_errno = errno; - r = TREE_ERROR_FATAL; - } else - t->working_dir_fd = te->symlink_parent_fd; + new_fd = te->symlink_parent_fd; + else + new_fd = open_on_current_dir(t, "..", O_RDONLY); + if (new_fd < 0) { + t->tree_errno = errno; + r = TREE_ERROR_FATAL; } else { - if (chdir("..") != 0) { - t->tree_errno = errno; - r = TREE_ERROR_FATAL; - } else - t->working_dir_fd = open(".", O_RDONLY); - } -#endif - if (r == 0) { + /* 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); @@ -1907,10 +2130,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); } @@ -1930,8 +2155,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); } @@ -2040,7 +2267,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); @@ -2111,6 +2339,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; @@ -2131,6 +2361,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; @@ -2152,7 +2384,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. */ @@ -2186,9 +2421,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 @@ -2214,7 +2453,8 @@ tree_target_is_same_as_parent(struct tree *t, const struct stat *st) struct tree_entry *te; for (te = t->current->parent; te != NULL; te = te->parent) { - if (te->dev == st->st_dev && te->ino == st->st_ino) + if (te->dev == (int64_t)st->st_dev && + te->ino == (int64_t)st->st_ino) return (1); } return (0); @@ -2231,7 +2471,8 @@ tree_current_is_symblic_link_target(struct tree *t) lst = tree_current_lstat(t); st = tree_current_stat(t); - return (st != NULL && st->st_dev == t->current_filesystem->dev && + return (st != NULL && lst != NULL && + (int64_t)st->st_dev == t->current_filesystem->dev && st->st_dev != lst->st_dev); }