summaryrefslogtreecommitdiffstats
path: root/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c')
-rw-r--r--contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c561
1 files changed, 498 insertions, 63 deletions
diff --git a/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c b/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c
index 7ada371..e9df514 100644
--- a/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c
+++ b/contrib/libarchive/libarchive/archive_read_disk_entry_from_file.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010 Michihiro NAKAJIMA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,6 +27,9 @@
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
+/* This is the tree-walking code for POSIX systems. */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+
#ifdef HAVE_SYS_TYPES_H
/* Mac OSX requires sys/types.h before sys/acl.h. */
#include <sys/types.h>
@@ -36,6 +40,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_EXTATTR_H
#include <sys/extattr.h>
#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
@@ -45,20 +52,48 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_XATTR_H
#include <sys/xattr.h>
#endif
+#ifdef HAVE_SYS_EA_H
+#include <sys/ea.h>
+#endif
#ifdef HAVE_ACL_LIBACL_H
#include <acl/libacl.h>
#endif
#ifdef HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#endif
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
+#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
+#ifdef HAVE_LINUX_FIEMAP_H
+#include <linux/fiemap.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#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 <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
#endif
#include "archive.h"
@@ -78,13 +113,18 @@ __FBSDID("$FreeBSD$");
static int setup_acls_posix1e(struct archive_read_disk *,
struct archive_entry *, int fd);
+static int setup_mac_metadata(struct archive_read_disk *,
+ struct archive_entry *, int fd);
static int setup_xattrs(struct archive_read_disk *,
struct archive_entry *, int fd);
+static int setup_sparse(struct archive_read_disk *,
+ struct archive_entry *, int fd);
int
archive_read_disk_entry_from_file(struct archive *_a,
struct archive_entry *entry,
- int fd, const struct stat *st)
+ int fd,
+ const struct stat *st)
{
struct archive_read_disk *a = (struct archive_read_disk *)_a;
const char *path, *name;
@@ -97,54 +137,35 @@ archive_read_disk_entry_from_file(struct archive *_a,
if (path == NULL)
path = archive_entry_pathname(entry);
-#ifdef EXT2_IOC_GETFLAGS
- /* Linux requires an extra ioctl to pull the flags. Although
- * 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(pathname, O_RDONLY | O_NONBLOCK | O_BINARY);
- if (fd >= 0) {
- unsigned long stflags;
- int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
- if (r == 0 && stflags != 0)
- archive_entry_set_fflags(entry, stflags, 0);
- }
- }
-#endif
-
- if (st == NULL) {
- /* TODO: On Windows, use GetFileInfoByHandle() here.
- * Using Windows stat() call is badly broken, but
- * even the stat() wrapper has problems because
- * 'struct stat' is broken on Windows.
- */
+ if (a->tree == NULL) {
+ if (st == NULL) {
#if HAVE_FSTAT
- if (fd >= 0) {
- if (fstat(fd, &s) != 0) {
- archive_set_error(&a->archive, errno,
- "Can't fstat");
- return (ARCHIVE_FAILED);
- }
- } else
+ if (fd >= 0) {
+ if (fstat(fd, &s) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't fstat");
+ return (ARCHIVE_FAILED);
+ }
+ } else
#endif
#if HAVE_LSTAT
- if (!a->follow_symlinks) {
- if (lstat(path, &s) != 0) {
+ if (!a->follow_symlinks) {
+ if (lstat(path, &s) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't lstat %s", path);
+ return (ARCHIVE_FAILED);
+ }
+ } else
+#endif
+ if (stat(path, &s) != 0) {
archive_set_error(&a->archive, errno,
- "Can't lstat %s", path);
+ "Can't stat %s", path);
return (ARCHIVE_FAILED);
}
- } else
-#endif
- if (stat(path, &s) != 0) {
- archive_set_error(&a->archive, errno,
- "Can't lstat %s", path);
- return (ARCHIVE_FAILED);
+ st = &s;
}
- st = &s;
+ archive_entry_copy_stat(entry, st);
}
- archive_entry_copy_stat(entry, st);
/* Lookup uname/gname */
name = archive_read_disk_uname(_a, archive_entry_uid(entry));
@@ -161,30 +182,185 @@ archive_read_disk_entry_from_file(struct archive *_a,
archive_entry_set_fflags(entry, st->st_flags, 0);
#endif
-#ifdef HAVE_READLINK
+#if defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)
+ /* Linux requires an extra ioctl to pull the flags. Although
+ * 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) {
+ unsigned long stflags;
+ r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
+ if (r == 0 && stflags != 0)
+ archive_entry_set_fflags(entry, stflags, 0);
+ }
+ }
+#endif
+
+#if defined(HAVE_READLINK) || defined(HAVE_READLINKAT)
if (S_ISLNK(st->st_mode)) {
- char linkbuffer[PATH_MAX + 1];
- int lnklen = readlink(path, linkbuffer, PATH_MAX);
+ size_t linkbuffer_len = st->st_size + 1;
+ char *linkbuffer;
+ int lnklen;
+
+ linkbuffer = malloc(linkbuffer_len);
+ if (linkbuffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't read link data");
+ return (ARCHIVE_FAILED);
+ }
+#ifdef HAVE_READLINKAT
+ if (a->entry_wd_fd >= 0)
+ lnklen = readlinkat(a->entry_wd_fd, path,
+ linkbuffer, linkbuffer_len);
+ else
+#endif /* HAVE_READLINKAT */
+ lnklen = readlink(path, linkbuffer, linkbuffer_len);
if (lnklen < 0) {
archive_set_error(&a->archive, errno,
"Couldn't read link data");
+ free(linkbuffer);
return (ARCHIVE_FAILED);
}
linkbuffer[lnklen] = 0;
archive_entry_set_symlink(entry, linkbuffer);
+ free(linkbuffer);
}
-#endif
+#endif /* HAVE_READLINK || HAVE_READLINKAT */
r = setup_acls_posix1e(a, entry, fd);
r1 = setup_xattrs(a, entry, fd);
if (r1 < r)
r = r1;
+ r1 = setup_mac_metadata(a, entry, fd);
+ if (r1 < r)
+ r = r1;
+ r1 = setup_sparse(a, entry, fd);
+ if (r1 < r)
+ r = r1;
+
/* If we opened the file earlier in this function, close it. */
if (initial_fd != fd)
close(fd);
return (r);
}
+#if defined(__APPLE__) && defined(HAVE_COPYFILE_H)
+/*
+ * The Mac OS "copyfile()" API copies the extended metadata for a
+ * file into a separate file in AppleDouble format (see RFC 1740).
+ *
+ * Mac OS tar and cpio implementations store this extended
+ * metadata as a separate entry just before the regular entry
+ * with a "._" prefix added to the filename.
+ *
+ * Note that this is currently done unconditionally; the tar program has
+ * an option to discard this information before the archive is written.
+ *
+ * TODO: If there's a failure, report it and return ARCHIVE_WARN.
+ */
+static int
+setup_mac_metadata(struct archive_read_disk *a,
+ struct archive_entry *entry, int fd)
+{
+ int tempfd = -1;
+ int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;
+ struct stat copyfile_stat;
+ int ret = ARCHIVE_OK;
+ void *buff;
+ int have_attrs;
+ const char *name, *tempdir, *tempfile = NULL;
+
+ name = archive_entry_sourcepath(entry);
+ if (name == NULL)
+ name = archive_entry_pathname(entry);
+ if (name == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't open file to read extended attributes: No name");
+ return (ARCHIVE_WARN);
+ }
+
+ /* Short-circuit if there's nothing to do. */
+ have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
+ if (have_attrs == -1) {
+ archive_set_error(&a->archive, errno,
+ "Could not check extended attributes");
+ return (ARCHIVE_WARN);
+ }
+ if (have_attrs == 0)
+ return (ARCHIVE_OK);
+
+ tempdir = NULL;
+ if (issetugid() == 0)
+ tempdir = getenv("TMPDIR");
+ if (tempdir == NULL)
+ tempdir = _PATH_TMP;
+ tempfile = tempnam(tempdir, "tar.md.");
+
+ /* XXX I wish copyfile() could pack directly to a memory
+ * buffer; that would avoid the temp file here. For that
+ * matter, it would be nice if fcopyfile() actually worked,
+ * that would reduce the many open/close races here. */
+ if (copyfile(name, tempfile, 0, copyfile_flags | COPYFILE_PACK)) {
+ archive_set_error(&a->archive, errno,
+ "Could not pack extended attributes");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ tempfd = open(tempfile, O_RDONLY);
+ if (tempfd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Could not open extended attribute file");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ if (fstat(tempfd, &copyfile_stat)) {
+ archive_set_error(&a->archive, errno,
+ "Could not check size of extended attributes");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ buff = malloc(copyfile_stat.st_size);
+ if (buff == NULL) {
+ archive_set_error(&a->archive, errno,
+ "Could not allocate memory for extended attributes");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) {
+ archive_set_error(&a->archive, errno,
+ "Could not read extended attributes into memory");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size);
+
+cleanup:
+ if (tempfd >= 0)
+ close(tempfd);
+ if (tempfile != NULL)
+ unlink(tempfile);
+ return (ret);
+}
+
+#else
+
+/*
+ * Stub implementation for non-Mac systems.
+ */
+static int
+setup_mac_metadata(struct archive_read_disk *a,
+ struct archive_entry *entry, int fd)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+ (void)fd; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+#endif
+
+
#ifdef HAVE_POSIX_ACL
static void setup_acl_posix1e(struct archive_read_disk *a,
struct archive_entry *entry, acl_t acl, int archive_entry_acl_type);
@@ -307,10 +483,12 @@ setup_acls_posix1e(struct archive_read_disk *a,
}
#endif
-#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR
+#if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \
+ HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \
+ (HAVE_FGETEA && HAVE_FLISTEA && HAVE_LISTEA)
/*
- * Linux extended attribute support.
+ * Linux and AIX extended attribute support.
*
* TODO: By using a stack-allocated buffer for the first
* call to getxattr(), we might be able to avoid the second
@@ -329,16 +507,25 @@ setup_xattr(struct archive_read_disk *a,
void *value = NULL;
const char *accpath;
- (void)fd; /* UNUSED */
-
accpath = archive_entry_sourcepath(entry);
if (accpath == NULL)
accpath = archive_entry_pathname(entry);
- if (!a->follow_symlinks)
+#if HAVE_FGETXATTR
+ if (fd >= 0)
+ size = fgetxattr(fd, name, NULL, 0);
+ else if (!a->follow_symlinks)
size = lgetxattr(accpath, name, NULL, 0);
else
size = getxattr(accpath, name, NULL, 0);
+#elif HAVE_FGETEA
+ if (fd >= 0)
+ size = fgetea(fd, name, NULL, 0);
+ else if (!a->follow_symlinks)
+ size = lgetea(accpath, name, NULL, 0);
+ else
+ size = getea(accpath, name, NULL, 0);
+#endif
if (size == -1) {
archive_set_error(&a->archive, errno,
@@ -351,10 +538,21 @@ setup_xattr(struct archive_read_disk *a,
return (ARCHIVE_FATAL);
}
- if (!a->follow_symlinks)
+#if HAVE_FGETXATTR
+ if (fd >= 0)
+ size = fgetxattr(fd, name, value, size);
+ else if (!a->follow_symlinks)
size = lgetxattr(accpath, name, value, size);
else
size = getxattr(accpath, name, value, size);
+#elif HAVE_FGETEA
+ if (fd >= 0)
+ size = fgetea(fd, name, value, size);
+ else if (!a->follow_symlinks)
+ size = lgetea(accpath, name, value, size);
+ else
+ size = getea(accpath, name, value, size);
+#endif
if (size == -1) {
archive_set_error(&a->archive, errno,
@@ -376,18 +574,28 @@ setup_xattrs(struct archive_read_disk *a,
const char *path;
ssize_t list_size;
-
path = archive_entry_sourcepath(entry);
if (path == NULL)
path = archive_entry_pathname(entry);
- if (!a->follow_symlinks)
+#if HAVE_FLISTXATTR
+ 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);
+ else if (!a->follow_symlinks)
+ list_size = llistea(path, NULL, 0);
+ else
+ list_size = listea(path, NULL, 0);
+#endif
if (list_size == -1) {
- if (errno == ENOTSUP)
+ if (errno == ENOTSUP || errno == ENOSYS)
return (ARCHIVE_OK);
archive_set_error(&a->archive, errno,
"Couldn't list extended attributes");
@@ -402,10 +610,21 @@ setup_xattrs(struct archive_read_disk *a,
return (ARCHIVE_FATAL);
}
- if (!a->follow_symlinks)
+#if HAVE_FLISTXATTR
+ 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);
+ else if (!a->follow_symlinks)
+ list_size = llistea(path, list, list_size);
+ else
+ list_size = listea(path, list, list_size);
+#endif
if (list_size == -1) {
archive_set_error(&a->archive, errno,
@@ -449,13 +668,13 @@ setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
void *value = NULL;
const char *accpath;
- (void)fd; /* UNUSED */
-
accpath = archive_entry_sourcepath(entry);
if (accpath == NULL)
accpath = archive_entry_pathname(entry);
- if (!a->follow_symlinks)
+ if (fd >= 0)
+ size = extattr_get_fd(fd, namespace, name, NULL, 0);
+ else if (!a->follow_symlinks)
size = extattr_get_link(accpath, namespace, name, NULL, 0);
else
size = extattr_get_file(accpath, namespace, name, NULL, 0);
@@ -471,7 +690,9 @@ setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
return (ARCHIVE_FATAL);
}
- if (!a->follow_symlinks)
+ if (fd >= 0)
+ size = extattr_get_fd(fd, namespace, name, value, size);
+ else if (!a->follow_symlinks)
size = extattr_get_link(accpath, namespace, name, value, size);
else
size = extattr_get_file(accpath, namespace, name, value, size);
@@ -502,7 +723,9 @@ setup_xattrs(struct archive_read_disk *a,
if (path == NULL)
path = archive_entry_pathname(entry);
- if (!a->follow_symlinks)
+ 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
list_size = extattr_list_file(path, namespace, NULL, 0);
@@ -523,7 +746,9 @@ setup_xattrs(struct archive_read_disk *a,
return (ARCHIVE_FATAL);
}
- if (!a->follow_symlinks)
+ 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
list_size = extattr_list_file(path, namespace, list, list_size);
@@ -568,3 +793,213 @@ setup_xattrs(struct archive_read_disk *a,
}
#endif
+
+#if defined(HAVE_LINUX_FIEMAP_H)
+
+/*
+ * Linux sparse interface.
+ *
+ * The FIEMAP ioctl returns an "extent" for each physical allocation
+ * on disk. We need to process those to generate a more compact list
+ * of logical file blocks. We also need to be very careful to use
+ * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes
+ * does not report allocations for newly-written data that hasn't
+ * been synced to disk.
+ *
+ * It's important to return a minimal sparse file list because we want
+ * to not trigger sparse file extensions if we don't have to, since
+ * not all readers support them.
+ */
+
+static int
+setup_sparse(struct archive_read_disk *a,
+ 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
+ || archive_entry_size(entry) <= 0
+ || archive_entry_hardlink(entry) != NULL)
+ return (ARCHIVE_OK);
+
+ 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) {
+ archive_set_error(&a->archive, errno,
+ "Can't open `%s'", path);
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe);
+ fm = (struct fiemap *)buff;
+ fm->fm_start = 0;
+ fm->fm_length = ~0ULL;;
+ fm->fm_flags = FIEMAP_FLAG_SYNC;
+ fm->fm_extent_count = count;
+ do_fiemap = 1;
+ size = archive_entry_size(entry);
+ for (;;) {
+ int i, r;
+
+ r = ioctl(fd, FS_IOC_FIEMAP, fm);
+ if (r < 0) {
+ /* When errno is ENOTTY, it is better we should
+ * return ARCHIVE_OK because an earlier version
+ *(<2.6.28) cannot perfom FS_IOC_FIEMAP.
+ * We should also check if errno is EOPNOTSUPP,
+ * it means "Operation not supported". */
+ if (errno != ENOTTY && errno != EOPNOTSUPP) {
+ archive_set_error(&a->archive, errno,
+ "FIEMAP failed");
+ exit_sts = ARCHIVE_FAILED;
+ }
+ goto exit_setup_sparse;
+ }
+ if (fm->fm_mapped_extents == 0)
+ break;
+ fe = fm->fm_extents;
+ for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) {
+ if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
+ /* The fe_length of the last block does not
+ * adjust itself to its size files. */
+ int64_t length = fe->fe_length;
+ if (fe->fe_logical + length > (uint64_t)size)
+ length -= fe->fe_logical + length - size;
+ if (fe->fe_logical == 0 && length == size) {
+ /* This is not sparse. */
+ do_fiemap = 0;
+ break;
+ }
+ if (length > 0)
+ archive_entry_sparse_add_entry(entry,
+ fe->fe_logical, length);
+ }
+ if (fe->fe_flags & FIEMAP_EXTENT_LAST)
+ do_fiemap = 0;
+ }
+ if (do_fiemap) {
+ fe = fm->fm_extents + fm->fm_mapped_extents -1;
+ fm->fm_start = fe->fe_logical + fe->fe_length;
+ } else
+ break;
+ }
+exit_setup_sparse:
+ if (initial_fd != fd)
+ close(fd);
+ return (exit_sts);
+}
+
+#elif defined(SEEK_HOLE) && defined(SEEK_DATA) && defined(_PC_MIN_HOLE_SIZE)
+
+/*
+ * FreeBSD and Solaris sparse interface.
+ */
+
+static int
+setup_sparse(struct archive_read_disk *a,
+ 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;
+
+ if (archive_entry_filetype(entry) != AE_IFREG
+ || archive_entry_size(entry) <= 0
+ || archive_entry_hardlink(entry) != NULL)
+ return (ARCHIVE_OK);
+
+ /* Does filesystem support the reporting of hole ? */
+ if (fd >= 0) {
+ if (fpathconf(fd, _PC_MIN_HOLE_SIZE) <= 0)
+ return (ARCHIVE_OK);
+ initial_off = lseek(fd, 0, SEEK_CUR);
+ if (initial_off != 0)
+ lseek(fd, 0, SEEK_SET);
+ } else {
+ const char *path;
+
+ 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) {
+ archive_set_error(&a->archive, errno,
+ "Can't open `%s'", path);
+ return (ARCHIVE_FAILED);
+ }
+ initial_off = 0;
+ }
+
+ off_s = 0;
+ size = archive_entry_size(entry);
+ while (off_s < size) {
+ off_s = lseek(fd, off_s, SEEK_DATA);
+ if (off_s == (off_t)-1) {
+ if (errno == ENXIO)
+ break;/* no more hole */
+ archive_set_error(&a->archive, errno,
+ "lseek(SEEK_HOLE) failed");
+ exit_sts = ARCHIVE_FAILED;
+ goto exit_setup_sparse;
+ }
+ off_e = lseek(fd, off_s, SEEK_HOLE);
+ if (off_s == (off_t)-1) {
+ if (errno == ENXIO) {
+ off_e = lseek(fd, 0, SEEK_END);
+ if (off_e != (off_t)-1)
+ break;/* no more data */
+ }
+ archive_set_error(&a->archive, errno,
+ "lseek(SEEK_DATA) failed");
+ exit_sts = ARCHIVE_FAILED;
+ goto exit_setup_sparse;
+ }
+ if (off_s == 0 && off_e == size)
+ break;/* This is not spase. */
+ archive_entry_sparse_add_entry(entry, off_s,
+ off_e - off_s);
+ off_s = off_e;
+ }
+exit_setup_sparse:
+ if (initial_fd != fd)
+ close(fd);
+ else
+ lseek(fd, initial_off, SEEK_SET);
+ return (exit_sts);
+}
+
+#else
+
+/*
+ * Generic (stub) sparse support.
+ */
+static int
+setup_sparse(struct archive_read_disk *a,
+ struct archive_entry *entry, int fd)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+ (void)fd; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#endif
+
+#endif /* !defined(_WIN32) || defined(__CYGWIN__) */
+
OpenPOWER on IntegriCloud