1caf54c4fSMartin Matuska /*- 2caf54c4fSMartin Matuska * Copyright (c) 2003-2009 Tim Kientzle 3fd082e96SMartin Matuska * Copyright (c) 2010-2012 Michihiro NAKAJIMA 4caf54c4fSMartin Matuska * All rights reserved. 5caf54c4fSMartin Matuska * 6caf54c4fSMartin Matuska * Redistribution and use in source and binary forms, with or without 7caf54c4fSMartin Matuska * modification, are permitted provided that the following conditions 8caf54c4fSMartin Matuska * are met: 9caf54c4fSMartin Matuska * 1. Redistributions of source code must retain the above copyright 10caf54c4fSMartin Matuska * notice, this list of conditions and the following disclaimer. 11caf54c4fSMartin Matuska * 2. Redistributions in binary form must reproduce the above copyright 12caf54c4fSMartin Matuska * notice, this list of conditions and the following disclaimer in the 13caf54c4fSMartin Matuska * documentation and/or other materials provided with the distribution. 14caf54c4fSMartin Matuska * 15caf54c4fSMartin Matuska * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16caf54c4fSMartin Matuska * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17caf54c4fSMartin Matuska * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18caf54c4fSMartin Matuska * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19caf54c4fSMartin Matuska * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20caf54c4fSMartin Matuska * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21caf54c4fSMartin Matuska * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22caf54c4fSMartin Matuska * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23caf54c4fSMartin Matuska * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24caf54c4fSMartin Matuska * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25caf54c4fSMartin Matuska */ 26caf54c4fSMartin Matuska 27caf54c4fSMartin Matuska #include "archive_platform.h" 286c22d9efSMartin Matuska __FBSDID("$FreeBSD$"); 29caf54c4fSMartin Matuska 306c95142eSMartin Matuska /* This is the tree-walking code for POSIX systems. */ 316c95142eSMartin Matuska #if !defined(_WIN32) || defined(__CYGWIN__) 326c95142eSMartin Matuska 33caf54c4fSMartin Matuska #ifdef HAVE_SYS_TYPES_H 34caf54c4fSMartin Matuska /* Mac OSX requires sys/types.h before sys/acl.h. */ 35caf54c4fSMartin Matuska #include <sys/types.h> 36caf54c4fSMartin Matuska #endif 37caf54c4fSMartin Matuska #ifdef HAVE_SYS_ACL_H 38caf54c4fSMartin Matuska #include <sys/acl.h> 39caf54c4fSMartin Matuska #endif 40caf54c4fSMartin Matuska #ifdef HAVE_SYS_EXTATTR_H 41caf54c4fSMartin Matuska #include <sys/extattr.h> 42caf54c4fSMartin Matuska #endif 436c95142eSMartin Matuska #ifdef HAVE_SYS_IOCTL_H 446c95142eSMartin Matuska #include <sys/ioctl.h> 456c95142eSMartin Matuska #endif 46caf54c4fSMartin Matuska #ifdef HAVE_SYS_PARAM_H 47caf54c4fSMartin Matuska #include <sys/param.h> 48caf54c4fSMartin Matuska #endif 49caf54c4fSMartin Matuska #ifdef HAVE_SYS_STAT_H 50caf54c4fSMartin Matuska #include <sys/stat.h> 51caf54c4fSMartin Matuska #endif 52acc60b03SMartin Matuska #if defined(HAVE_SYS_XATTR_H) 53caf54c4fSMartin Matuska #include <sys/xattr.h> 54acc60b03SMartin Matuska #elif defined(HAVE_ATTR_XATTR_H) 55acc60b03SMartin Matuska #include <attr/xattr.h> 56caf54c4fSMartin Matuska #endif 576c95142eSMartin Matuska #ifdef HAVE_SYS_EA_H 586c95142eSMartin Matuska #include <sys/ea.h> 596c95142eSMartin Matuska #endif 60caf54c4fSMartin Matuska #ifdef HAVE_ACL_LIBACL_H 61caf54c4fSMartin Matuska #include <acl/libacl.h> 62caf54c4fSMartin Matuska #endif 636c95142eSMartin Matuska #ifdef HAVE_COPYFILE_H 646c95142eSMartin Matuska #include <copyfile.h> 656c95142eSMartin Matuska #endif 66caf54c4fSMartin Matuska #ifdef HAVE_ERRNO_H 67caf54c4fSMartin Matuska #include <errno.h> 68caf54c4fSMartin Matuska #endif 696c95142eSMartin Matuska #ifdef HAVE_FCNTL_H 706c95142eSMartin Matuska #include <fcntl.h> 716c95142eSMartin Matuska #endif 72caf54c4fSMartin Matuska #ifdef HAVE_LIMITS_H 73caf54c4fSMartin Matuska #include <limits.h> 74caf54c4fSMartin Matuska #endif 75fd082e96SMartin Matuska #ifdef HAVE_LINUX_TYPES_H 76fd082e96SMartin Matuska #include <linux/types.h> 77fd082e96SMartin Matuska #endif 786c95142eSMartin Matuska #ifdef HAVE_LINUX_FIEMAP_H 796c95142eSMartin Matuska #include <linux/fiemap.h> 806c95142eSMartin Matuska #endif 816c95142eSMartin Matuska #ifdef HAVE_LINUX_FS_H 826c95142eSMartin Matuska #include <linux/fs.h> 836c95142eSMartin Matuska #endif 846c95142eSMartin Matuska /* 856c95142eSMartin Matuska * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. 866c95142eSMartin Matuska * As the include guards don't agree, the order of include is important. 876c95142eSMartin Matuska */ 886c95142eSMartin Matuska #ifdef HAVE_LINUX_EXT2_FS_H 896c95142eSMartin Matuska #include <linux/ext2_fs.h> /* for Linux file flags */ 906c95142eSMartin Matuska #endif 916c95142eSMartin Matuska #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) 926c95142eSMartin Matuska #include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */ 936c95142eSMartin Matuska #endif 946c95142eSMartin Matuska #ifdef HAVE_PATHS_H 956c95142eSMartin Matuska #include <paths.h> 966c95142eSMartin Matuska #endif 976c95142eSMartin Matuska #ifdef HAVE_UNISTD_H 986c95142eSMartin Matuska #include <unistd.h> 99caf54c4fSMartin Matuska #endif 100caf54c4fSMartin Matuska 101caf54c4fSMartin Matuska #include "archive.h" 102caf54c4fSMartin Matuska #include "archive_entry.h" 103caf54c4fSMartin Matuska #include "archive_private.h" 104caf54c4fSMartin Matuska #include "archive_read_disk_private.h" 105caf54c4fSMartin Matuska 106acc60b03SMartin Matuska #ifndef O_CLOEXEC 107acc60b03SMartin Matuska #define O_CLOEXEC 0 108acc60b03SMartin Matuska #endif 109acc60b03SMartin Matuska 110caf54c4fSMartin Matuska /* 111caf54c4fSMartin Matuska * Linux and FreeBSD plug this obvious hole in POSIX.1e in 112caf54c4fSMartin Matuska * different ways. 113caf54c4fSMartin Matuska */ 114caf54c4fSMartin Matuska #if HAVE_ACL_GET_PERM 115caf54c4fSMartin Matuska #define ACL_GET_PERM acl_get_perm 116caf54c4fSMartin Matuska #elif HAVE_ACL_GET_PERM_NP 117caf54c4fSMartin Matuska #define ACL_GET_PERM acl_get_perm_np 118caf54c4fSMartin Matuska #endif 119caf54c4fSMartin Matuska 12010ed66fdSMartin Matuska static int setup_acls(struct archive_read_disk *, 121fd082e96SMartin Matuska struct archive_entry *, int *fd); 1226c95142eSMartin Matuska static int setup_mac_metadata(struct archive_read_disk *, 123fd082e96SMartin Matuska struct archive_entry *, int *fd); 124caf54c4fSMartin Matuska static int setup_xattrs(struct archive_read_disk *, 125fd082e96SMartin Matuska struct archive_entry *, int *fd); 1266c95142eSMartin Matuska static int setup_sparse(struct archive_read_disk *, 127fd082e96SMartin Matuska struct archive_entry *, int *fd); 128caf54c4fSMartin Matuska 129caf54c4fSMartin Matuska int 130caf54c4fSMartin Matuska archive_read_disk_entry_from_file(struct archive *_a, 131caf54c4fSMartin Matuska struct archive_entry *entry, 1326c95142eSMartin Matuska int fd, 1336c95142eSMartin Matuska const struct stat *st) 134caf54c4fSMartin Matuska { 135caf54c4fSMartin Matuska struct archive_read_disk *a = (struct archive_read_disk *)_a; 136caf54c4fSMartin Matuska const char *path, *name; 137caf54c4fSMartin Matuska struct stat s; 138caf54c4fSMartin Matuska int initial_fd = fd; 139caf54c4fSMartin Matuska int r, r1; 140caf54c4fSMartin Matuska 141caf54c4fSMartin Matuska archive_clear_error(_a); 142caf54c4fSMartin Matuska path = archive_entry_sourcepath(entry); 143caf54c4fSMartin Matuska if (path == NULL) 144caf54c4fSMartin Matuska path = archive_entry_pathname(entry); 145caf54c4fSMartin Matuska 1466c95142eSMartin Matuska if (a->tree == NULL) { 147caf54c4fSMartin Matuska if (st == NULL) { 148caf54c4fSMartin Matuska #if HAVE_FSTAT 149caf54c4fSMartin Matuska if (fd >= 0) { 150caf54c4fSMartin Matuska if (fstat(fd, &s) != 0) { 151caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 152caf54c4fSMartin Matuska "Can't fstat"); 153caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 154caf54c4fSMartin Matuska } 155caf54c4fSMartin Matuska } else 156caf54c4fSMartin Matuska #endif 157caf54c4fSMartin Matuska #if HAVE_LSTAT 158caf54c4fSMartin Matuska if (!a->follow_symlinks) { 159caf54c4fSMartin Matuska if (lstat(path, &s) != 0) { 160caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 161caf54c4fSMartin Matuska "Can't lstat %s", path); 162caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 163caf54c4fSMartin Matuska } 164caf54c4fSMartin Matuska } else 165caf54c4fSMartin Matuska #endif 166caf54c4fSMartin Matuska if (stat(path, &s) != 0) { 167caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 1686c95142eSMartin Matuska "Can't stat %s", path); 169caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 170caf54c4fSMartin Matuska } 171caf54c4fSMartin Matuska st = &s; 172caf54c4fSMartin Matuska } 173caf54c4fSMartin Matuska archive_entry_copy_stat(entry, st); 17410ed66fdSMartin Matuska } 17510ed66fdSMartin Matuska 176caf54c4fSMartin Matuska /* Lookup uname/gname */ 177caf54c4fSMartin Matuska name = archive_read_disk_uname(_a, archive_entry_uid(entry)); 178caf54c4fSMartin Matuska if (name != NULL) 179caf54c4fSMartin Matuska archive_entry_copy_uname(entry, name); 180caf54c4fSMartin Matuska name = archive_read_disk_gname(_a, archive_entry_gid(entry)); 181caf54c4fSMartin Matuska if (name != NULL) 182caf54c4fSMartin Matuska archive_entry_copy_gname(entry, name); 183caf54c4fSMartin Matuska 184caf54c4fSMartin Matuska #ifdef HAVE_STRUCT_STAT_ST_FLAGS 185caf54c4fSMartin Matuska /* On FreeBSD, we get flags for free with the stat. */ 186caf54c4fSMartin Matuska /* TODO: Does this belong in copy_stat()? */ 187caf54c4fSMartin Matuska if (st->st_flags != 0) 188caf54c4fSMartin Matuska archive_entry_set_fflags(entry, st->st_flags, 0); 189caf54c4fSMartin Matuska #endif 190caf54c4fSMartin Matuska 1916c95142eSMartin Matuska #if defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) 1926c95142eSMartin Matuska /* Linux requires an extra ioctl to pull the flags. Although 1936c95142eSMartin Matuska * this is an extra step, it has a nice side-effect: We get an 1946c95142eSMartin Matuska * open file descriptor which we can use in the subsequent lookups. */ 1956c95142eSMartin Matuska if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { 196fd082e96SMartin Matuska if (fd < 0) { 197fd082e96SMartin Matuska if (a->tree != NULL) 198fd082e96SMartin Matuska fd = a->open_on_current_dir(a->tree, path, 199acc60b03SMartin Matuska O_RDONLY | O_NONBLOCK | O_CLOEXEC); 200fd082e96SMartin Matuska else 201acc60b03SMartin Matuska fd = open(path, O_RDONLY | O_NONBLOCK | 202acc60b03SMartin Matuska O_CLOEXEC); 203acc60b03SMartin Matuska __archive_ensure_cloexec_flag(fd); 204fd082e96SMartin Matuska } 2056c95142eSMartin Matuska if (fd >= 0) { 206acc60b03SMartin Matuska int stflags; 2076c95142eSMartin Matuska r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); 2086c95142eSMartin Matuska if (r == 0 && stflags != 0) 2096c95142eSMartin Matuska archive_entry_set_fflags(entry, stflags, 0); 2106c95142eSMartin Matuska } 2116c95142eSMartin Matuska } 2126c95142eSMartin Matuska #endif 2136c95142eSMartin Matuska 2146c95142eSMartin Matuska #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) 215caf54c4fSMartin Matuska if (S_ISLNK(st->st_mode)) { 2166c95142eSMartin Matuska size_t linkbuffer_len = st->st_size + 1; 2176c95142eSMartin Matuska char *linkbuffer; 2186c95142eSMartin Matuska int lnklen; 2196c95142eSMartin Matuska 2206c95142eSMartin Matuska linkbuffer = malloc(linkbuffer_len); 2216c95142eSMartin Matuska if (linkbuffer == NULL) { 2226c95142eSMartin Matuska archive_set_error(&a->archive, ENOMEM, 2236c95142eSMartin Matuska "Couldn't read link data"); 2246c95142eSMartin Matuska return (ARCHIVE_FAILED); 2256c95142eSMartin Matuska } 226fd082e96SMartin Matuska if (a->tree != NULL) { 2276c95142eSMartin Matuska #ifdef HAVE_READLINKAT 228fd082e96SMartin Matuska lnklen = readlinkat(a->tree_current_dir_fd(a->tree), 229fd082e96SMartin Matuska path, linkbuffer, linkbuffer_len); 230fd082e96SMartin Matuska #else 231fd082e96SMartin Matuska if (a->tree_enter_working_dir(a->tree) != 0) { 232fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 233fd082e96SMartin Matuska "Couldn't read link data"); 234fd082e96SMartin Matuska free(linkbuffer); 235fd082e96SMartin Matuska return (ARCHIVE_FAILED); 236fd082e96SMartin Matuska } 237fd082e96SMartin Matuska lnklen = readlink(path, linkbuffer, linkbuffer_len); 2386c95142eSMartin Matuska #endif /* HAVE_READLINKAT */ 239fd082e96SMartin Matuska } else 2406c95142eSMartin Matuska lnklen = readlink(path, linkbuffer, linkbuffer_len); 241caf54c4fSMartin Matuska if (lnklen < 0) { 242caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 243caf54c4fSMartin Matuska "Couldn't read link data"); 2446c95142eSMartin Matuska free(linkbuffer); 245caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 246caf54c4fSMartin Matuska } 247caf54c4fSMartin Matuska linkbuffer[lnklen] = 0; 248caf54c4fSMartin Matuska archive_entry_set_symlink(entry, linkbuffer); 2496c95142eSMartin Matuska free(linkbuffer); 250caf54c4fSMartin Matuska } 2516c95142eSMartin Matuska #endif /* HAVE_READLINK || HAVE_READLINKAT */ 252caf54c4fSMartin Matuska 25310ed66fdSMartin Matuska r = setup_acls(a, entry, &fd); 254cdf63a70SMartin Matuska if (!a->suppress_xattr) { 255fd082e96SMartin Matuska r1 = setup_xattrs(a, entry, &fd); 256caf54c4fSMartin Matuska if (r1 < r) 257caf54c4fSMartin Matuska r = r1; 258cdf63a70SMartin Matuska } 259fd082e96SMartin Matuska if (a->enable_copyfile) { 260fd082e96SMartin Matuska r1 = setup_mac_metadata(a, entry, &fd); 2616c95142eSMartin Matuska if (r1 < r) 2626c95142eSMartin Matuska r = r1; 263fd082e96SMartin Matuska } 264fd082e96SMartin Matuska r1 = setup_sparse(a, entry, &fd); 2656c95142eSMartin Matuska if (r1 < r) 2666c95142eSMartin Matuska r = r1; 2676c95142eSMartin Matuska 268caf54c4fSMartin Matuska /* If we opened the file earlier in this function, close it. */ 269caf54c4fSMartin Matuska if (initial_fd != fd) 270caf54c4fSMartin Matuska close(fd); 271caf54c4fSMartin Matuska return (r); 272caf54c4fSMartin Matuska } 273caf54c4fSMartin Matuska 2746c95142eSMartin Matuska #if defined(__APPLE__) && defined(HAVE_COPYFILE_H) 2756c95142eSMartin Matuska /* 2766c95142eSMartin Matuska * The Mac OS "copyfile()" API copies the extended metadata for a 2776c95142eSMartin Matuska * file into a separate file in AppleDouble format (see RFC 1740). 2786c95142eSMartin Matuska * 2796c95142eSMartin Matuska * Mac OS tar and cpio implementations store this extended 2806c95142eSMartin Matuska * metadata as a separate entry just before the regular entry 2816c95142eSMartin Matuska * with a "._" prefix added to the filename. 2826c95142eSMartin Matuska * 2836c95142eSMartin Matuska * Note that this is currently done unconditionally; the tar program has 2846c95142eSMartin Matuska * an option to discard this information before the archive is written. 2856c95142eSMartin Matuska * 2866c95142eSMartin Matuska * TODO: If there's a failure, report it and return ARCHIVE_WARN. 2876c95142eSMartin Matuska */ 2886c95142eSMartin Matuska static int 2896c95142eSMartin Matuska setup_mac_metadata(struct archive_read_disk *a, 290fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 2916c95142eSMartin Matuska { 2926c95142eSMartin Matuska int tempfd = -1; 2936c95142eSMartin Matuska int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR; 2946c95142eSMartin Matuska struct stat copyfile_stat; 2956c95142eSMartin Matuska int ret = ARCHIVE_OK; 296acc60b03SMartin Matuska void *buff = NULL; 2976c95142eSMartin Matuska int have_attrs; 298acc60b03SMartin Matuska const char *name, *tempdir; 299acc60b03SMartin Matuska struct archive_string tempfile; 3006c95142eSMartin Matuska 301fd082e96SMartin Matuska (void)fd; /* UNUSED */ 3026c95142eSMartin Matuska name = archive_entry_sourcepath(entry); 3036c95142eSMartin Matuska if (name == NULL) 3046c95142eSMartin Matuska name = archive_entry_pathname(entry); 3056c95142eSMartin Matuska if (name == NULL) { 3066c95142eSMartin Matuska archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 3076c95142eSMartin Matuska "Can't open file to read extended attributes: No name"); 3086c95142eSMartin Matuska return (ARCHIVE_WARN); 3096c95142eSMartin Matuska } 3106c95142eSMartin Matuska 311fd082e96SMartin Matuska if (a->tree != NULL) { 312fd082e96SMartin Matuska if (a->tree_enter_working_dir(a->tree) != 0) { 313fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 314fd082e96SMartin Matuska "Couldn't change dir"); 315fd082e96SMartin Matuska return (ARCHIVE_FAILED); 316fd082e96SMartin Matuska } 317fd082e96SMartin Matuska } 318fd082e96SMartin Matuska 3196c95142eSMartin Matuska /* Short-circuit if there's nothing to do. */ 3206c95142eSMartin Matuska have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK); 3216c95142eSMartin Matuska if (have_attrs == -1) { 3226c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3236c95142eSMartin Matuska "Could not check extended attributes"); 3246c95142eSMartin Matuska return (ARCHIVE_WARN); 3256c95142eSMartin Matuska } 3266c95142eSMartin Matuska if (have_attrs == 0) 3276c95142eSMartin Matuska return (ARCHIVE_OK); 3286c95142eSMartin Matuska 3296c95142eSMartin Matuska tempdir = NULL; 3306c95142eSMartin Matuska if (issetugid() == 0) 3316c95142eSMartin Matuska tempdir = getenv("TMPDIR"); 3326c95142eSMartin Matuska if (tempdir == NULL) 3336c95142eSMartin Matuska tempdir = _PATH_TMP; 334acc60b03SMartin Matuska archive_string_init(&tempfile); 335acc60b03SMartin Matuska archive_strcpy(&tempfile, tempdir); 336acc60b03SMartin Matuska archive_strcat(&tempfile, "tar.md.XXXXXX"); 337acc60b03SMartin Matuska tempfd = mkstemp(tempfile.s); 338acc60b03SMartin Matuska if (tempfd < 0) { 339acc60b03SMartin Matuska archive_set_error(&a->archive, errno, 340acc60b03SMartin Matuska "Could not open extended attribute file"); 341acc60b03SMartin Matuska ret = ARCHIVE_WARN; 342acc60b03SMartin Matuska goto cleanup; 343acc60b03SMartin Matuska } 344acc60b03SMartin Matuska __archive_ensure_cloexec_flag(tempfd); 3456c95142eSMartin Matuska 3466c95142eSMartin Matuska /* XXX I wish copyfile() could pack directly to a memory 3476c95142eSMartin Matuska * buffer; that would avoid the temp file here. For that 3486c95142eSMartin Matuska * matter, it would be nice if fcopyfile() actually worked, 3496c95142eSMartin Matuska * that would reduce the many open/close races here. */ 350acc60b03SMartin Matuska if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) { 3516c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3526c95142eSMartin Matuska "Could not pack extended attributes"); 3536c95142eSMartin Matuska ret = ARCHIVE_WARN; 3546c95142eSMartin Matuska goto cleanup; 3556c95142eSMartin Matuska } 3566c95142eSMartin Matuska if (fstat(tempfd, ©file_stat)) { 3576c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3586c95142eSMartin Matuska "Could not check size of extended attributes"); 3596c95142eSMartin Matuska ret = ARCHIVE_WARN; 3606c95142eSMartin Matuska goto cleanup; 3616c95142eSMartin Matuska } 3626c95142eSMartin Matuska buff = malloc(copyfile_stat.st_size); 3636c95142eSMartin Matuska if (buff == NULL) { 3646c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3656c95142eSMartin Matuska "Could not allocate memory for extended attributes"); 3666c95142eSMartin Matuska ret = ARCHIVE_WARN; 3676c95142eSMartin Matuska goto cleanup; 3686c95142eSMartin Matuska } 3696c95142eSMartin Matuska if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) { 3706c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3716c95142eSMartin Matuska "Could not read extended attributes into memory"); 3726c95142eSMartin Matuska ret = ARCHIVE_WARN; 3736c95142eSMartin Matuska goto cleanup; 3746c95142eSMartin Matuska } 3756c95142eSMartin Matuska archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size); 3766c95142eSMartin Matuska 3776c95142eSMartin Matuska cleanup: 378acc60b03SMartin Matuska if (tempfd >= 0) { 3796c95142eSMartin Matuska close(tempfd); 380acc60b03SMartin Matuska unlink(tempfile.s); 381acc60b03SMartin Matuska } 382acc60b03SMartin Matuska archive_string_free(&tempfile); 383acc60b03SMartin Matuska free(buff); 3846c95142eSMartin Matuska return (ret); 3856c95142eSMartin Matuska } 3866c95142eSMartin Matuska 3876c95142eSMartin Matuska #else 3886c95142eSMartin Matuska 3896c95142eSMartin Matuska /* 3906c95142eSMartin Matuska * Stub implementation for non-Mac systems. 3916c95142eSMartin Matuska */ 3926c95142eSMartin Matuska static int 3936c95142eSMartin Matuska setup_mac_metadata(struct archive_read_disk *a, 394fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 3956c95142eSMartin Matuska { 3966c95142eSMartin Matuska (void)a; /* UNUSED */ 3976c95142eSMartin Matuska (void)entry; /* UNUSED */ 3986c95142eSMartin Matuska (void)fd; /* UNUSED */ 3996c95142eSMartin Matuska return (ARCHIVE_OK); 4006c95142eSMartin Matuska } 4016c95142eSMartin Matuska #endif 4026c95142eSMartin Matuska 4036c95142eSMartin Matuska 404cdf63a70SMartin Matuska #ifdef HAVE_POSIX_ACL 40510ed66fdSMartin Matuska static int translate_acl(struct archive_read_disk *a, 406caf54c4fSMartin Matuska struct archive_entry *entry, acl_t acl, int archive_entry_acl_type); 407caf54c4fSMartin Matuska 408caf54c4fSMartin Matuska static int 40910ed66fdSMartin Matuska setup_acls(struct archive_read_disk *a, 410fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 411caf54c4fSMartin Matuska { 412caf54c4fSMartin Matuska const char *accpath; 413caf54c4fSMartin Matuska acl_t acl; 414c438d384SMartin Matuska #if HAVE_ACL_IS_TRIVIAL_NP 41510ed66fdSMartin Matuska int r; 416c438d384SMartin Matuska #endif 417caf54c4fSMartin Matuska 418caf54c4fSMartin Matuska accpath = archive_entry_sourcepath(entry); 419caf54c4fSMartin Matuska if (accpath == NULL) 420caf54c4fSMartin Matuska accpath = archive_entry_pathname(entry); 421caf54c4fSMartin Matuska 422caf54c4fSMartin Matuska archive_entry_acl_clear(entry); 423caf54c4fSMartin Matuska 424cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 42510ed66fdSMartin Matuska /* Try NFS4 ACL first. */ 42610ed66fdSMartin Matuska if (*fd >= 0) 427c438d384SMartin Matuska acl = acl_get_fd(*fd); 42810ed66fdSMartin Matuska #if HAVE_ACL_GET_LINK_NP 429c438d384SMartin Matuska else if (!a->follow_symlinks) 43010ed66fdSMartin Matuska acl = acl_get_link_np(accpath, ACL_TYPE_NFS4); 43110ed66fdSMartin Matuska #else 432c438d384SMartin Matuska else if ((!a->follow_symlinks) 433c438d384SMartin Matuska && (archive_entry_filetype(entry) == AE_IFLNK)) 434c438d384SMartin Matuska /* We can't get the ACL of a symlink, so we assume it can't 435c438d384SMartin Matuska have one. */ 43610ed66fdSMartin Matuska acl = NULL; 43710ed66fdSMartin Matuska #endif 438c438d384SMartin Matuska else 439c4676089SMartin Matuska acl = acl_get_file(accpath, ACL_TYPE_NFS4); 440c4676089SMartin Matuska #if HAVE_ACL_IS_TRIVIAL_NP 441c4676089SMartin Matuska /* Ignore "trivial" ACLs that just mirror the file mode. */ 442c438d384SMartin Matuska acl_is_trivial_np(acl, &r); 443c4676089SMartin Matuska if (r) { 44410ed66fdSMartin Matuska acl_free(acl); 445c4676089SMartin Matuska acl = NULL; 446c4676089SMartin Matuska } 447c4676089SMartin Matuska #endif 448c438d384SMartin Matuska if (acl != NULL) { 449c438d384SMartin Matuska translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); 450c4676089SMartin Matuska acl_free(acl); 451c438d384SMartin Matuska return (ARCHIVE_OK); 452c4676089SMartin Matuska } 453cdf63a70SMartin Matuska #endif 454fd082e96SMartin Matuska 455caf54c4fSMartin Matuska /* Retrieve access ACL from file. */ 456fd082e96SMartin Matuska if (*fd >= 0) 457fd082e96SMartin Matuska acl = acl_get_fd(*fd); 458caf54c4fSMartin Matuska #if HAVE_ACL_GET_LINK_NP 459c438d384SMartin Matuska else if (!a->follow_symlinks) 460caf54c4fSMartin Matuska acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); 461caf54c4fSMartin Matuska #else 462c438d384SMartin Matuska else if ((!a->follow_symlinks) 463c438d384SMartin Matuska && (archive_entry_filetype(entry) == AE_IFLNK)) 464c438d384SMartin Matuska /* We can't get the ACL of a symlink, so we assume it can't 465c438d384SMartin Matuska have one. */ 466caf54c4fSMartin Matuska acl = NULL; 467caf54c4fSMartin Matuska #endif 468c438d384SMartin Matuska else 469caf54c4fSMartin Matuska acl = acl_get_file(accpath, ACL_TYPE_ACCESS); 470caf54c4fSMartin Matuska if (acl != NULL) { 471c438d384SMartin Matuska translate_acl(a, entry, acl, 472caf54c4fSMartin Matuska ARCHIVE_ENTRY_ACL_TYPE_ACCESS); 473caf54c4fSMartin Matuska acl_free(acl); 474caf54c4fSMartin Matuska } 475caf54c4fSMartin Matuska 476caf54c4fSMartin Matuska /* Only directories can have default ACLs. */ 477caf54c4fSMartin Matuska if (S_ISDIR(archive_entry_mode(entry))) { 478caf54c4fSMartin Matuska acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); 479caf54c4fSMartin Matuska if (acl != NULL) { 480c438d384SMartin Matuska translate_acl(a, entry, acl, 481caf54c4fSMartin Matuska ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); 482caf54c4fSMartin Matuska acl_free(acl); 483caf54c4fSMartin Matuska } 484caf54c4fSMartin Matuska } 485caf54c4fSMartin Matuska return (ARCHIVE_OK); 486caf54c4fSMartin Matuska } 487caf54c4fSMartin Matuska 488caf54c4fSMartin Matuska /* 48910ed66fdSMartin Matuska * Translate system ACL into libarchive internal structure. 490caf54c4fSMartin Matuska */ 49110ed66fdSMartin Matuska 49210ed66fdSMartin Matuska static struct { 49310ed66fdSMartin Matuska int archive_perm; 49410ed66fdSMartin Matuska int platform_perm; 49510ed66fdSMartin Matuska } acl_perm_map[] = { 49610ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, 49710ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, 49810ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, 499cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 50010ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, 50110ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, 50210ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, 50310ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, 50410ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, 50510ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, 50610ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, 50710ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, 50810ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, 50910ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, 51010ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, 51110ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, 51210ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL}, 51310ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, 51410ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, 51510ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} 516cdf63a70SMartin Matuska #endif 51710ed66fdSMartin Matuska }; 51810ed66fdSMartin Matuska 519cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 52010ed66fdSMartin Matuska static struct { 52110ed66fdSMartin Matuska int archive_inherit; 52210ed66fdSMartin Matuska int platform_inherit; 52310ed66fdSMartin Matuska } acl_inherit_map[] = { 52410ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, 52510ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, 52610ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, 52710ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY} 52810ed66fdSMartin Matuska }; 529cdf63a70SMartin Matuska #endif 53010ed66fdSMartin Matuska static int 53110ed66fdSMartin Matuska translate_acl(struct archive_read_disk *a, 53210ed66fdSMartin Matuska struct archive_entry *entry, acl_t acl, int default_entry_acl_type) 533caf54c4fSMartin Matuska { 534caf54c4fSMartin Matuska acl_tag_t acl_tag; 535cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 53610ed66fdSMartin Matuska acl_entry_type_t acl_type; 53710ed66fdSMartin Matuska acl_flagset_t acl_flagset; 538c438d384SMartin Matuska int brand, r; 539cdf63a70SMartin Matuska #endif 540caf54c4fSMartin Matuska acl_entry_t acl_entry; 541caf54c4fSMartin Matuska acl_permset_t acl_permset; 542cdf63a70SMartin Matuska int i, entry_acl_type; 543c438d384SMartin Matuska int s, ae_id, ae_tag, ae_perm; 544caf54c4fSMartin Matuska const char *ae_name; 545caf54c4fSMartin Matuska 546cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 54710ed66fdSMartin Matuska // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 54810ed66fdSMartin Matuska // Make sure the "brand" on this ACL is consistent 54910ed66fdSMartin Matuska // with the default_entry_acl_type bits provided. 550c438d384SMartin Matuska acl_get_brand_np(acl, &brand); 55110ed66fdSMartin Matuska switch (brand) { 55210ed66fdSMartin Matuska case ACL_BRAND_POSIX: 55310ed66fdSMartin Matuska switch (default_entry_acl_type) { 55410ed66fdSMartin Matuska case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: 55510ed66fdSMartin Matuska case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: 55610ed66fdSMartin Matuska break; 55710ed66fdSMartin Matuska default: 558c438d384SMartin Matuska // XXX set warning message? 559c438d384SMartin Matuska return ARCHIVE_FAILED; 56010ed66fdSMartin Matuska } 56110ed66fdSMartin Matuska break; 56210ed66fdSMartin Matuska case ACL_BRAND_NFS4: 56310ed66fdSMartin Matuska if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 564c438d384SMartin Matuska // XXX set warning message? 565c438d384SMartin Matuska return ARCHIVE_FAILED; 56610ed66fdSMartin Matuska } 56710ed66fdSMartin Matuska break; 56810ed66fdSMartin Matuska default: 569c438d384SMartin Matuska // XXX set warning message? 570c438d384SMartin Matuska return ARCHIVE_FAILED; 57110ed66fdSMartin Matuska break; 57210ed66fdSMartin Matuska } 573cdf63a70SMartin Matuska #endif 57410ed66fdSMartin Matuska 57510ed66fdSMartin Matuska 576caf54c4fSMartin Matuska s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); 577caf54c4fSMartin Matuska while (s == 1) { 578caf54c4fSMartin Matuska ae_id = -1; 579caf54c4fSMartin Matuska ae_name = NULL; 58010ed66fdSMartin Matuska ae_perm = 0; 581caf54c4fSMartin Matuska 582c438d384SMartin Matuska acl_get_tag_type(acl_entry, &acl_tag); 58310ed66fdSMartin Matuska switch (acl_tag) { 58410ed66fdSMartin Matuska case ACL_USER: 585caf54c4fSMartin Matuska ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); 586caf54c4fSMartin Matuska ae_name = archive_read_disk_uname(&a->archive, ae_id); 587caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_USER; 58810ed66fdSMartin Matuska break; 58910ed66fdSMartin Matuska case ACL_GROUP: 590caf54c4fSMartin Matuska ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); 591caf54c4fSMartin Matuska ae_name = archive_read_disk_gname(&a->archive, ae_id); 592caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_GROUP; 59310ed66fdSMartin Matuska break; 59410ed66fdSMartin Matuska case ACL_MASK: 595caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_MASK; 59610ed66fdSMartin Matuska break; 59710ed66fdSMartin Matuska case ACL_USER_OBJ: 598caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 59910ed66fdSMartin Matuska break; 60010ed66fdSMartin Matuska case ACL_GROUP_OBJ: 601caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 60210ed66fdSMartin Matuska break; 60310ed66fdSMartin Matuska case ACL_OTHER: 604caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_OTHER; 60510ed66fdSMartin Matuska break; 606cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 60710ed66fdSMartin Matuska case ACL_EVERYONE: 60810ed66fdSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; 60910ed66fdSMartin Matuska break; 610cdf63a70SMartin Matuska #endif 61110ed66fdSMartin Matuska default: 612caf54c4fSMartin Matuska /* Skip types that libarchive can't support. */ 61388b860fcSMartin Matuska s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); 614caf54c4fSMartin Matuska continue; 615caf54c4fSMartin Matuska } 616caf54c4fSMartin Matuska 61710ed66fdSMartin Matuska // XXX acl type maps to allow/deny/audit/YYYY bits 61810ed66fdSMartin Matuska // XXX acl_get_entry_type_np on FreeBSD returns EINVAL for 61910ed66fdSMartin Matuska // non-NFSv4 ACLs 62010ed66fdSMartin Matuska entry_acl_type = default_entry_acl_type; 621cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 622c438d384SMartin Matuska r = acl_get_entry_type_np(acl_entry, &acl_type); 623c438d384SMartin Matuska if (r == 0) { 62410ed66fdSMartin Matuska switch (acl_type) { 62510ed66fdSMartin Matuska case ACL_ENTRY_TYPE_ALLOW: 62610ed66fdSMartin Matuska entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; 62710ed66fdSMartin Matuska break; 62810ed66fdSMartin Matuska case ACL_ENTRY_TYPE_DENY: 62910ed66fdSMartin Matuska entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; 63010ed66fdSMartin Matuska break; 63110ed66fdSMartin Matuska case ACL_ENTRY_TYPE_AUDIT: 63210ed66fdSMartin Matuska entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; 63310ed66fdSMartin Matuska break; 63410ed66fdSMartin Matuska case ACL_ENTRY_TYPE_ALARM: 63510ed66fdSMartin Matuska entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; 63610ed66fdSMartin Matuska break; 637c438d384SMartin Matuska } 63810ed66fdSMartin Matuska } 63910ed66fdSMartin Matuska 64010ed66fdSMartin Matuska /* 64110ed66fdSMartin Matuska * Libarchive stores "flag" (NFSv4 inheritance bits) 64210ed66fdSMartin Matuska * in the ae_perm bitmap. 64310ed66fdSMartin Matuska */ 644c438d384SMartin Matuska // XXX acl_get_flagset_np on FreeBSD returns EINVAL for 645c438d384SMartin Matuska // non-NFSv4 ACLs 646c438d384SMartin Matuska r = acl_get_flagset_np(acl_entry, &acl_flagset); 647c438d384SMartin Matuska if (r == 0) { 64810ed66fdSMartin Matuska for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { 649c438d384SMartin Matuska if (acl_get_flag_np(acl_flagset, 650c438d384SMartin Matuska acl_inherit_map[i].platform_inherit)) 65110ed66fdSMartin Matuska ae_perm |= acl_inherit_map[i].archive_inherit; 652cfa49a9bSMartin Matuska } 65310ed66fdSMartin Matuska } 654cdf63a70SMartin Matuska #endif 65510ed66fdSMartin Matuska 656c438d384SMartin Matuska acl_get_permset(acl_entry, &acl_permset); 65710ed66fdSMartin Matuska for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { 658caf54c4fSMartin Matuska /* 659caf54c4fSMartin Matuska * acl_get_perm() is spelled differently on different 660caf54c4fSMartin Matuska * platforms; see above. 661caf54c4fSMartin Matuska */ 662c438d384SMartin Matuska if (ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm)) 66310ed66fdSMartin Matuska ae_perm |= acl_perm_map[i].archive_perm; 66410ed66fdSMartin Matuska } 665caf54c4fSMartin Matuska 666c438d384SMartin Matuska archive_entry_acl_add_entry(entry, entry_acl_type, 66710ed66fdSMartin Matuska ae_perm, ae_tag, 668caf54c4fSMartin Matuska ae_id, ae_name); 669caf54c4fSMartin Matuska 670caf54c4fSMartin Matuska s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); 671caf54c4fSMartin Matuska } 67210ed66fdSMartin Matuska return (ARCHIVE_OK); 673caf54c4fSMartin Matuska } 674caf54c4fSMartin Matuska #else 675caf54c4fSMartin Matuska static int 67610ed66fdSMartin Matuska setup_acls(struct archive_read_disk *a, 677acc60b03SMartin Matuska struct archive_entry *entry, int *fd) 678caf54c4fSMartin Matuska { 679caf54c4fSMartin Matuska (void)a; /* UNUSED */ 680caf54c4fSMartin Matuska (void)entry; /* UNUSED */ 681caf54c4fSMartin Matuska (void)fd; /* UNUSED */ 682caf54c4fSMartin Matuska return (ARCHIVE_OK); 683caf54c4fSMartin Matuska } 684caf54c4fSMartin Matuska #endif 685caf54c4fSMartin Matuska 6866c95142eSMartin Matuska #if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \ 6876c95142eSMartin Matuska HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \ 6886c95142eSMartin Matuska (HAVE_FGETEA && HAVE_FLISTEA && HAVE_LISTEA) 689caf54c4fSMartin Matuska 690caf54c4fSMartin Matuska /* 6916c95142eSMartin Matuska * Linux and AIX extended attribute support. 692caf54c4fSMartin Matuska * 693caf54c4fSMartin Matuska * TODO: By using a stack-allocated buffer for the first 694caf54c4fSMartin Matuska * call to getxattr(), we might be able to avoid the second 695caf54c4fSMartin Matuska * call entirely. We only need the second call if the 696caf54c4fSMartin Matuska * stack-allocated buffer is too small. But a modest buffer 697caf54c4fSMartin Matuska * of 1024 bytes or so will often be big enough. Same applies 698caf54c4fSMartin Matuska * to listxattr(). 699caf54c4fSMartin Matuska */ 700caf54c4fSMartin Matuska 701caf54c4fSMartin Matuska 702caf54c4fSMartin Matuska static int 703caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, 704caf54c4fSMartin Matuska struct archive_entry *entry, const char *name, int fd) 705caf54c4fSMartin Matuska { 706caf54c4fSMartin Matuska ssize_t size; 707caf54c4fSMartin Matuska void *value = NULL; 708caf54c4fSMartin Matuska const char *accpath; 709caf54c4fSMartin Matuska 710caf54c4fSMartin Matuska accpath = archive_entry_sourcepath(entry); 711caf54c4fSMartin Matuska if (accpath == NULL) 712caf54c4fSMartin Matuska accpath = archive_entry_pathname(entry); 713caf54c4fSMartin Matuska 7146c95142eSMartin Matuska #if HAVE_FGETXATTR 7156c95142eSMartin Matuska if (fd >= 0) 7166c95142eSMartin Matuska size = fgetxattr(fd, name, NULL, 0); 7176c95142eSMartin Matuska else if (!a->follow_symlinks) 718caf54c4fSMartin Matuska size = lgetxattr(accpath, name, NULL, 0); 719caf54c4fSMartin Matuska else 720caf54c4fSMartin Matuska size = getxattr(accpath, name, NULL, 0); 7216c95142eSMartin Matuska #elif HAVE_FGETEA 7226c95142eSMartin Matuska if (fd >= 0) 7236c95142eSMartin Matuska size = fgetea(fd, name, NULL, 0); 7246c95142eSMartin Matuska else if (!a->follow_symlinks) 7256c95142eSMartin Matuska size = lgetea(accpath, name, NULL, 0); 7266c95142eSMartin Matuska else 7276c95142eSMartin Matuska size = getea(accpath, name, NULL, 0); 7286c95142eSMartin Matuska #endif 729caf54c4fSMartin Matuska 730caf54c4fSMartin Matuska if (size == -1) { 731caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 732caf54c4fSMartin Matuska "Couldn't query extended attribute"); 733caf54c4fSMartin Matuska return (ARCHIVE_WARN); 734caf54c4fSMartin Matuska } 735caf54c4fSMartin Matuska 736caf54c4fSMartin Matuska if (size > 0 && (value = malloc(size)) == NULL) { 737caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 738caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 739caf54c4fSMartin Matuska } 740caf54c4fSMartin Matuska 7416c95142eSMartin Matuska #if HAVE_FGETXATTR 7426c95142eSMartin Matuska if (fd >= 0) 7436c95142eSMartin Matuska size = fgetxattr(fd, name, value, size); 7446c95142eSMartin Matuska else if (!a->follow_symlinks) 745caf54c4fSMartin Matuska size = lgetxattr(accpath, name, value, size); 746caf54c4fSMartin Matuska else 747caf54c4fSMartin Matuska size = getxattr(accpath, name, value, size); 7486c95142eSMartin Matuska #elif HAVE_FGETEA 7496c95142eSMartin Matuska if (fd >= 0) 7506c95142eSMartin Matuska size = fgetea(fd, name, value, size); 7516c95142eSMartin Matuska else if (!a->follow_symlinks) 7526c95142eSMartin Matuska size = lgetea(accpath, name, value, size); 7536c95142eSMartin Matuska else 7546c95142eSMartin Matuska size = getea(accpath, name, value, size); 7556c95142eSMartin Matuska #endif 756caf54c4fSMartin Matuska 757caf54c4fSMartin Matuska if (size == -1) { 758caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 759caf54c4fSMartin Matuska "Couldn't read extended attribute"); 760caf54c4fSMartin Matuska return (ARCHIVE_WARN); 761caf54c4fSMartin Matuska } 762caf54c4fSMartin Matuska 763caf54c4fSMartin Matuska archive_entry_xattr_add_entry(entry, name, value, size); 764caf54c4fSMartin Matuska 765caf54c4fSMartin Matuska free(value); 766caf54c4fSMartin Matuska return (ARCHIVE_OK); 767caf54c4fSMartin Matuska } 768caf54c4fSMartin Matuska 769caf54c4fSMartin Matuska static int 770caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a, 771fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 772caf54c4fSMartin Matuska { 773caf54c4fSMartin Matuska char *list, *p; 774caf54c4fSMartin Matuska const char *path; 775caf54c4fSMartin Matuska ssize_t list_size; 776caf54c4fSMartin Matuska 777caf54c4fSMartin Matuska path = archive_entry_sourcepath(entry); 778caf54c4fSMartin Matuska if (path == NULL) 779caf54c4fSMartin Matuska path = archive_entry_pathname(entry); 780caf54c4fSMartin Matuska 781fd082e96SMartin Matuska if (*fd < 0 && a->tree != NULL) { 782fd082e96SMartin Matuska if (a->follow_symlinks || 783fd082e96SMartin Matuska archive_entry_filetype(entry) != AE_IFLNK) 784fd082e96SMartin Matuska *fd = a->open_on_current_dir(a->tree, path, 785fd082e96SMartin Matuska O_RDONLY | O_NONBLOCK); 786fd082e96SMartin Matuska if (*fd < 0) { 787fd082e96SMartin Matuska if (a->tree_enter_working_dir(a->tree) != 0) { 788fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 789fd082e96SMartin Matuska "Couldn't access %s", path); 790fd082e96SMartin Matuska return (ARCHIVE_FAILED); 791fd082e96SMartin Matuska } 792fd082e96SMartin Matuska } 793fd082e96SMartin Matuska } 794fd082e96SMartin Matuska 7956c95142eSMartin Matuska #if HAVE_FLISTXATTR 796fd082e96SMartin Matuska if (*fd >= 0) 797fd082e96SMartin Matuska list_size = flistxattr(*fd, NULL, 0); 7986c95142eSMartin Matuska else if (!a->follow_symlinks) 799caf54c4fSMartin Matuska list_size = llistxattr(path, NULL, 0); 800caf54c4fSMartin Matuska else 801caf54c4fSMartin Matuska list_size = listxattr(path, NULL, 0); 8026c95142eSMartin Matuska #elif HAVE_FLISTEA 803fd082e96SMartin Matuska if (*fd >= 0) 804fd082e96SMartin Matuska list_size = flistea(*fd, NULL, 0); 8056c95142eSMartin Matuska else if (!a->follow_symlinks) 8066c95142eSMartin Matuska list_size = llistea(path, NULL, 0); 8076c95142eSMartin Matuska else 8086c95142eSMartin Matuska list_size = listea(path, NULL, 0); 8096c95142eSMartin Matuska #endif 810caf54c4fSMartin Matuska 811caf54c4fSMartin Matuska if (list_size == -1) { 8126c95142eSMartin Matuska if (errno == ENOTSUP || errno == ENOSYS) 813caf54c4fSMartin Matuska return (ARCHIVE_OK); 814caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 815caf54c4fSMartin Matuska "Couldn't list extended attributes"); 816caf54c4fSMartin Matuska return (ARCHIVE_WARN); 817caf54c4fSMartin Matuska } 818caf54c4fSMartin Matuska 819caf54c4fSMartin Matuska if (list_size == 0) 820caf54c4fSMartin Matuska return (ARCHIVE_OK); 821caf54c4fSMartin Matuska 822caf54c4fSMartin Matuska if ((list = malloc(list_size)) == NULL) { 823caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 824caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 825caf54c4fSMartin Matuska } 826caf54c4fSMartin Matuska 8276c95142eSMartin Matuska #if HAVE_FLISTXATTR 828fd082e96SMartin Matuska if (*fd >= 0) 829fd082e96SMartin Matuska list_size = flistxattr(*fd, list, list_size); 8306c95142eSMartin Matuska else if (!a->follow_symlinks) 831caf54c4fSMartin Matuska list_size = llistxattr(path, list, list_size); 832caf54c4fSMartin Matuska else 833caf54c4fSMartin Matuska list_size = listxattr(path, list, list_size); 8346c95142eSMartin Matuska #elif HAVE_FLISTEA 835fd082e96SMartin Matuska if (*fd >= 0) 836fd082e96SMartin Matuska list_size = flistea(*fd, list, list_size); 8376c95142eSMartin Matuska else if (!a->follow_symlinks) 8386c95142eSMartin Matuska list_size = llistea(path, list, list_size); 8396c95142eSMartin Matuska else 8406c95142eSMartin Matuska list_size = listea(path, list, list_size); 8416c95142eSMartin Matuska #endif 842caf54c4fSMartin Matuska 843caf54c4fSMartin Matuska if (list_size == -1) { 844caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 845caf54c4fSMartin Matuska "Couldn't retrieve extended attributes"); 846caf54c4fSMartin Matuska free(list); 847caf54c4fSMartin Matuska return (ARCHIVE_WARN); 848caf54c4fSMartin Matuska } 849caf54c4fSMartin Matuska 850caf54c4fSMartin Matuska for (p = list; (p - list) < list_size; p += strlen(p) + 1) { 851caf54c4fSMartin Matuska if (strncmp(p, "system.", 7) == 0 || 852caf54c4fSMartin Matuska strncmp(p, "xfsroot.", 8) == 0) 853caf54c4fSMartin Matuska continue; 854fd082e96SMartin Matuska setup_xattr(a, entry, p, *fd); 855caf54c4fSMartin Matuska } 856caf54c4fSMartin Matuska 857caf54c4fSMartin Matuska free(list); 858caf54c4fSMartin Matuska return (ARCHIVE_OK); 859caf54c4fSMartin Matuska } 860caf54c4fSMartin Matuska 861caf54c4fSMartin Matuska #elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE && \ 862caf54c4fSMartin Matuska HAVE_DECL_EXTATTR_NAMESPACE_USER 863caf54c4fSMartin Matuska 864caf54c4fSMartin Matuska /* 865caf54c4fSMartin Matuska * FreeBSD extattr interface. 866caf54c4fSMartin Matuska */ 867caf54c4fSMartin Matuska 868caf54c4fSMartin Matuska /* TODO: Implement this. Follow the Linux model above, but 869caf54c4fSMartin Matuska * with FreeBSD-specific system calls, of course. Be careful 870caf54c4fSMartin Matuska * to not include the system extattrs that hold ACLs; we handle 871caf54c4fSMartin Matuska * those separately. 872caf54c4fSMartin Matuska */ 873caf54c4fSMartin Matuska static int 874caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, 875caf54c4fSMartin Matuska int namespace, const char *name, const char *fullname, int fd); 876caf54c4fSMartin Matuska 877caf54c4fSMartin Matuska static int 878caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, 879caf54c4fSMartin Matuska int namespace, const char *name, const char *fullname, int fd) 880caf54c4fSMartin Matuska { 881caf54c4fSMartin Matuska ssize_t size; 882caf54c4fSMartin Matuska void *value = NULL; 883caf54c4fSMartin Matuska const char *accpath; 884caf54c4fSMartin Matuska 885caf54c4fSMartin Matuska accpath = archive_entry_sourcepath(entry); 886caf54c4fSMartin Matuska if (accpath == NULL) 887caf54c4fSMartin Matuska accpath = archive_entry_pathname(entry); 888caf54c4fSMartin Matuska 8896c95142eSMartin Matuska if (fd >= 0) 8906c95142eSMartin Matuska size = extattr_get_fd(fd, namespace, name, NULL, 0); 8916c95142eSMartin Matuska else if (!a->follow_symlinks) 892caf54c4fSMartin Matuska size = extattr_get_link(accpath, namespace, name, NULL, 0); 893caf54c4fSMartin Matuska else 894caf54c4fSMartin Matuska size = extattr_get_file(accpath, namespace, name, NULL, 0); 895caf54c4fSMartin Matuska 896caf54c4fSMartin Matuska if (size == -1) { 897caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 898caf54c4fSMartin Matuska "Couldn't query extended attribute"); 899caf54c4fSMartin Matuska return (ARCHIVE_WARN); 900caf54c4fSMartin Matuska } 901caf54c4fSMartin Matuska 902caf54c4fSMartin Matuska if (size > 0 && (value = malloc(size)) == NULL) { 903caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 904caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 905caf54c4fSMartin Matuska } 906caf54c4fSMartin Matuska 9076c95142eSMartin Matuska if (fd >= 0) 9086c95142eSMartin Matuska size = extattr_get_fd(fd, namespace, name, value, size); 9096c95142eSMartin Matuska else if (!a->follow_symlinks) 910caf54c4fSMartin Matuska size = extattr_get_link(accpath, namespace, name, value, size); 911caf54c4fSMartin Matuska else 912caf54c4fSMartin Matuska size = extattr_get_file(accpath, namespace, name, value, size); 913caf54c4fSMartin Matuska 914caf54c4fSMartin Matuska if (size == -1) { 915fd082e96SMartin Matuska free(value); 916caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 917caf54c4fSMartin Matuska "Couldn't read extended attribute"); 918caf54c4fSMartin Matuska return (ARCHIVE_WARN); 919caf54c4fSMartin Matuska } 920caf54c4fSMartin Matuska 921caf54c4fSMartin Matuska archive_entry_xattr_add_entry(entry, fullname, value, size); 922caf54c4fSMartin Matuska 923caf54c4fSMartin Matuska free(value); 924caf54c4fSMartin Matuska return (ARCHIVE_OK); 925caf54c4fSMartin Matuska } 926caf54c4fSMartin Matuska 927caf54c4fSMartin Matuska static int 928caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a, 929fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 930caf54c4fSMartin Matuska { 931caf54c4fSMartin Matuska char buff[512]; 932caf54c4fSMartin Matuska char *list, *p; 933caf54c4fSMartin Matuska ssize_t list_size; 934caf54c4fSMartin Matuska const char *path; 935caf54c4fSMartin Matuska int namespace = EXTATTR_NAMESPACE_USER; 936caf54c4fSMartin Matuska 937caf54c4fSMartin Matuska path = archive_entry_sourcepath(entry); 938caf54c4fSMartin Matuska if (path == NULL) 939caf54c4fSMartin Matuska path = archive_entry_pathname(entry); 940caf54c4fSMartin Matuska 941fd082e96SMartin Matuska if (*fd < 0 && a->tree != NULL) { 942fd082e96SMartin Matuska if (a->follow_symlinks || 943fd082e96SMartin Matuska archive_entry_filetype(entry) != AE_IFLNK) 944fd082e96SMartin Matuska *fd = a->open_on_current_dir(a->tree, path, 945fd082e96SMartin Matuska O_RDONLY | O_NONBLOCK); 946fd082e96SMartin Matuska if (*fd < 0) { 947fd082e96SMartin Matuska if (a->tree_enter_working_dir(a->tree) != 0) { 948fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 949fd082e96SMartin Matuska "Couldn't access %s", path); 950fd082e96SMartin Matuska return (ARCHIVE_FAILED); 951fd082e96SMartin Matuska } 952fd082e96SMartin Matuska } 953fd082e96SMartin Matuska } 954fd082e96SMartin Matuska 955fd082e96SMartin Matuska if (*fd >= 0) 956fd082e96SMartin Matuska list_size = extattr_list_fd(*fd, namespace, NULL, 0); 9576c95142eSMartin Matuska else if (!a->follow_symlinks) 958caf54c4fSMartin Matuska list_size = extattr_list_link(path, namespace, NULL, 0); 959caf54c4fSMartin Matuska else 960caf54c4fSMartin Matuska list_size = extattr_list_file(path, namespace, NULL, 0); 961caf54c4fSMartin Matuska 962caf54c4fSMartin Matuska if (list_size == -1 && errno == EOPNOTSUPP) 963caf54c4fSMartin Matuska return (ARCHIVE_OK); 964caf54c4fSMartin Matuska if (list_size == -1) { 965caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 966caf54c4fSMartin Matuska "Couldn't list extended attributes"); 967caf54c4fSMartin Matuska return (ARCHIVE_WARN); 968caf54c4fSMartin Matuska } 969caf54c4fSMartin Matuska 970caf54c4fSMartin Matuska if (list_size == 0) 971caf54c4fSMartin Matuska return (ARCHIVE_OK); 972caf54c4fSMartin Matuska 973caf54c4fSMartin Matuska if ((list = malloc(list_size)) == NULL) { 974caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 975caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 976caf54c4fSMartin Matuska } 977caf54c4fSMartin Matuska 978fd082e96SMartin Matuska if (*fd >= 0) 979fd082e96SMartin Matuska list_size = extattr_list_fd(*fd, namespace, list, list_size); 9806c95142eSMartin Matuska else if (!a->follow_symlinks) 981caf54c4fSMartin Matuska list_size = extattr_list_link(path, namespace, list, list_size); 982caf54c4fSMartin Matuska else 983caf54c4fSMartin Matuska list_size = extattr_list_file(path, namespace, list, list_size); 984caf54c4fSMartin Matuska 985caf54c4fSMartin Matuska if (list_size == -1) { 986caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 987caf54c4fSMartin Matuska "Couldn't retrieve extended attributes"); 988caf54c4fSMartin Matuska free(list); 989caf54c4fSMartin Matuska return (ARCHIVE_WARN); 990caf54c4fSMartin Matuska } 991caf54c4fSMartin Matuska 992caf54c4fSMartin Matuska p = list; 993caf54c4fSMartin Matuska while ((p - list) < list_size) { 994caf54c4fSMartin Matuska size_t len = 255 & (int)*p; 995caf54c4fSMartin Matuska char *name; 996caf54c4fSMartin Matuska 997caf54c4fSMartin Matuska strcpy(buff, "user."); 998caf54c4fSMartin Matuska name = buff + strlen(buff); 999caf54c4fSMartin Matuska memcpy(name, p + 1, len); 1000caf54c4fSMartin Matuska name[len] = '\0'; 1001fd082e96SMartin Matuska setup_xattr(a, entry, namespace, name, buff, *fd); 1002caf54c4fSMartin Matuska p += 1 + len; 1003caf54c4fSMartin Matuska } 1004caf54c4fSMartin Matuska 1005caf54c4fSMartin Matuska free(list); 1006caf54c4fSMartin Matuska return (ARCHIVE_OK); 1007caf54c4fSMartin Matuska } 1008caf54c4fSMartin Matuska 1009caf54c4fSMartin Matuska #else 1010caf54c4fSMartin Matuska 1011caf54c4fSMartin Matuska /* 1012caf54c4fSMartin Matuska * Generic (stub) extended attribute support. 1013caf54c4fSMartin Matuska */ 1014caf54c4fSMartin Matuska static int 1015caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a, 1016fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 1017caf54c4fSMartin Matuska { 1018caf54c4fSMartin Matuska (void)a; /* UNUSED */ 1019caf54c4fSMartin Matuska (void)entry; /* UNUSED */ 1020caf54c4fSMartin Matuska (void)fd; /* UNUSED */ 1021caf54c4fSMartin Matuska return (ARCHIVE_OK); 1022caf54c4fSMartin Matuska } 1023caf54c4fSMartin Matuska 1024caf54c4fSMartin Matuska #endif 10256c95142eSMartin Matuska 10266c95142eSMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H) 10276c95142eSMartin Matuska 10286c95142eSMartin Matuska /* 10296c95142eSMartin Matuska * Linux sparse interface. 10306c95142eSMartin Matuska * 10316c95142eSMartin Matuska * The FIEMAP ioctl returns an "extent" for each physical allocation 10326c95142eSMartin Matuska * on disk. We need to process those to generate a more compact list 10336c95142eSMartin Matuska * of logical file blocks. We also need to be very careful to use 10346c95142eSMartin Matuska * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes 10356c95142eSMartin Matuska * does not report allocations for newly-written data that hasn't 10366c95142eSMartin Matuska * been synced to disk. 10376c95142eSMartin Matuska * 10386c95142eSMartin Matuska * It's important to return a minimal sparse file list because we want 10396c95142eSMartin Matuska * to not trigger sparse file extensions if we don't have to, since 10406c95142eSMartin Matuska * not all readers support them. 10416c95142eSMartin Matuska */ 10426c95142eSMartin Matuska 10436c95142eSMartin Matuska static int 10446c95142eSMartin Matuska setup_sparse(struct archive_read_disk *a, 1045fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 10466c95142eSMartin Matuska { 10476c95142eSMartin Matuska char buff[4096]; 10486c95142eSMartin Matuska struct fiemap *fm; 10496c95142eSMartin Matuska struct fiemap_extent *fe; 10506c95142eSMartin Matuska int64_t size; 1051cdf63a70SMartin Matuska int count, do_fiemap, iters; 10526c95142eSMartin Matuska int exit_sts = ARCHIVE_OK; 10536c95142eSMartin Matuska 10546c95142eSMartin Matuska if (archive_entry_filetype(entry) != AE_IFREG 10556c95142eSMartin Matuska || archive_entry_size(entry) <= 0 10566c95142eSMartin Matuska || archive_entry_hardlink(entry) != NULL) 10576c95142eSMartin Matuska return (ARCHIVE_OK); 10586c95142eSMartin Matuska 1059fd082e96SMartin Matuska if (*fd < 0) { 10606c95142eSMartin Matuska const char *path; 10616c95142eSMartin Matuska 10626c95142eSMartin Matuska path = archive_entry_sourcepath(entry); 10636c95142eSMartin Matuska if (path == NULL) 10646c95142eSMartin Matuska path = archive_entry_pathname(entry); 1065fd082e96SMartin Matuska if (a->tree != NULL) 1066fd082e96SMartin Matuska *fd = a->open_on_current_dir(a->tree, path, 1067acc60b03SMartin Matuska O_RDONLY | O_NONBLOCK | O_CLOEXEC); 1068fd082e96SMartin Matuska else 1069acc60b03SMartin Matuska *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); 1070fd082e96SMartin Matuska if (*fd < 0) { 10716c95142eSMartin Matuska archive_set_error(&a->archive, errno, 10726c95142eSMartin Matuska "Can't open `%s'", path); 10736c95142eSMartin Matuska return (ARCHIVE_FAILED); 10746c95142eSMartin Matuska } 1075acc60b03SMartin Matuska __archive_ensure_cloexec_flag(*fd); 10766c95142eSMartin Matuska } 10776c95142eSMartin Matuska 1078acc60b03SMartin Matuska /* Initialize buffer to avoid the error valgrind complains about. */ 1079acc60b03SMartin Matuska memset(buff, 0, sizeof(buff)); 10806c95142eSMartin Matuska count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe); 10816c95142eSMartin Matuska fm = (struct fiemap *)buff; 10826c95142eSMartin Matuska fm->fm_start = 0; 10836c95142eSMartin Matuska fm->fm_length = ~0ULL;; 10846c95142eSMartin Matuska fm->fm_flags = FIEMAP_FLAG_SYNC; 10856c95142eSMartin Matuska fm->fm_extent_count = count; 10866c95142eSMartin Matuska do_fiemap = 1; 10876c95142eSMartin Matuska size = archive_entry_size(entry); 1088cdf63a70SMartin Matuska for (iters = 0; ; ++iters) { 10896c95142eSMartin Matuska int i, r; 10906c95142eSMartin Matuska 1091fd082e96SMartin Matuska r = ioctl(*fd, FS_IOC_FIEMAP, fm); 10926c95142eSMartin Matuska if (r < 0) { 1093fd082e96SMartin Matuska /* When something error happens, it is better we 1094fd082e96SMartin Matuska * should return ARCHIVE_OK because an earlier 1095fd082e96SMartin Matuska * version(<2.6.28) cannot perfom FS_IOC_FIEMAP. */ 10966c95142eSMartin Matuska goto exit_setup_sparse; 10976c95142eSMartin Matuska } 1098cdf63a70SMartin Matuska if (fm->fm_mapped_extents == 0) { 1099cdf63a70SMartin Matuska if (iters == 0) { 1100cdf63a70SMartin Matuska /* Fully sparse file; insert a zero-length "data" entry */ 1101cdf63a70SMartin Matuska archive_entry_sparse_add_entry(entry, 0, 0); 1102cdf63a70SMartin Matuska } 11036c95142eSMartin Matuska break; 1104cdf63a70SMartin Matuska } 11056c95142eSMartin Matuska fe = fm->fm_extents; 11066c95142eSMartin Matuska for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) { 11076c95142eSMartin Matuska if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) { 11086c95142eSMartin Matuska /* The fe_length of the last block does not 11096c95142eSMartin Matuska * adjust itself to its size files. */ 11106c95142eSMartin Matuska int64_t length = fe->fe_length; 11116c95142eSMartin Matuska if (fe->fe_logical + length > (uint64_t)size) 11126c95142eSMartin Matuska length -= fe->fe_logical + length - size; 11136c95142eSMartin Matuska if (fe->fe_logical == 0 && length == size) { 11146c95142eSMartin Matuska /* This is not sparse. */ 11156c95142eSMartin Matuska do_fiemap = 0; 11166c95142eSMartin Matuska break; 11176c95142eSMartin Matuska } 11186c95142eSMartin Matuska if (length > 0) 11196c95142eSMartin Matuska archive_entry_sparse_add_entry(entry, 11206c95142eSMartin Matuska fe->fe_logical, length); 11216c95142eSMartin Matuska } 11226c95142eSMartin Matuska if (fe->fe_flags & FIEMAP_EXTENT_LAST) 11236c95142eSMartin Matuska do_fiemap = 0; 11246c95142eSMartin Matuska } 11256c95142eSMartin Matuska if (do_fiemap) { 11266c95142eSMartin Matuska fe = fm->fm_extents + fm->fm_mapped_extents -1; 11276c95142eSMartin Matuska fm->fm_start = fe->fe_logical + fe->fe_length; 11286c95142eSMartin Matuska } else 11296c95142eSMartin Matuska break; 11306c95142eSMartin Matuska } 11316c95142eSMartin Matuska exit_setup_sparse: 11326c95142eSMartin Matuska return (exit_sts); 11336c95142eSMartin Matuska } 11346c95142eSMartin Matuska 11356c95142eSMartin Matuska #elif defined(SEEK_HOLE) && defined(SEEK_DATA) && defined(_PC_MIN_HOLE_SIZE) 11366c95142eSMartin Matuska 11376c95142eSMartin Matuska /* 11386c95142eSMartin Matuska * FreeBSD and Solaris sparse interface. 11396c95142eSMartin Matuska */ 11406c95142eSMartin Matuska 11416c95142eSMartin Matuska static int 11426c95142eSMartin Matuska setup_sparse(struct archive_read_disk *a, 1143fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 11446c95142eSMartin Matuska { 11456c95142eSMartin Matuska int64_t size; 11466c95142eSMartin Matuska off_t initial_off; /* FreeBSD/Solaris only, so off_t okay here */ 11476c95142eSMartin Matuska off_t off_s, off_e; /* FreeBSD/Solaris only, so off_t okay here */ 11486c95142eSMartin Matuska int exit_sts = ARCHIVE_OK; 1149cdf63a70SMartin Matuska int check_fully_sparse = 0; 11506c95142eSMartin Matuska 11516c95142eSMartin Matuska if (archive_entry_filetype(entry) != AE_IFREG 11526c95142eSMartin Matuska || archive_entry_size(entry) <= 0 11536c95142eSMartin Matuska || archive_entry_hardlink(entry) != NULL) 11546c95142eSMartin Matuska return (ARCHIVE_OK); 11556c95142eSMartin Matuska 11566c95142eSMartin Matuska /* Does filesystem support the reporting of hole ? */ 1157fd082e96SMartin Matuska if (*fd < 0 && a->tree != NULL) { 1158fd082e96SMartin Matuska const char *path; 1159fd082e96SMartin Matuska 1160fd082e96SMartin Matuska path = archive_entry_sourcepath(entry); 1161fd082e96SMartin Matuska if (path == NULL) 1162fd082e96SMartin Matuska path = archive_entry_pathname(entry); 1163fd082e96SMartin Matuska *fd = a->open_on_current_dir(a->tree, path, 1164fd082e96SMartin Matuska O_RDONLY | O_NONBLOCK); 1165fd082e96SMartin Matuska if (*fd < 0) { 1166fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 1167fd082e96SMartin Matuska "Can't open `%s'", path); 1168fd082e96SMartin Matuska return (ARCHIVE_FAILED); 1169fd082e96SMartin Matuska } 1170fd082e96SMartin Matuska } 1171fd082e96SMartin Matuska 1172fd082e96SMartin Matuska if (*fd >= 0) { 1173fd082e96SMartin Matuska if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0) 11746c95142eSMartin Matuska return (ARCHIVE_OK); 1175fd082e96SMartin Matuska initial_off = lseek(*fd, 0, SEEK_CUR); 11766c95142eSMartin Matuska if (initial_off != 0) 1177fd082e96SMartin Matuska lseek(*fd, 0, SEEK_SET); 11786c95142eSMartin Matuska } else { 11796c95142eSMartin Matuska const char *path; 11806c95142eSMartin Matuska 11816c95142eSMartin Matuska path = archive_entry_sourcepath(entry); 11826c95142eSMartin Matuska if (path == NULL) 11836c95142eSMartin Matuska path = archive_entry_pathname(entry); 1184fd082e96SMartin Matuska 11856c95142eSMartin Matuska if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0) 11866c95142eSMartin Matuska return (ARCHIVE_OK); 1187acc60b03SMartin Matuska *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); 1188fd082e96SMartin Matuska if (*fd < 0) { 11896c95142eSMartin Matuska archive_set_error(&a->archive, errno, 11906c95142eSMartin Matuska "Can't open `%s'", path); 11916c95142eSMartin Matuska return (ARCHIVE_FAILED); 11926c95142eSMartin Matuska } 1193acc60b03SMartin Matuska __archive_ensure_cloexec_flag(*fd); 11946c95142eSMartin Matuska initial_off = 0; 11956c95142eSMartin Matuska } 11966c95142eSMartin Matuska 11976c95142eSMartin Matuska off_s = 0; 11986c95142eSMartin Matuska size = archive_entry_size(entry); 11996c95142eSMartin Matuska while (off_s < size) { 1200fd082e96SMartin Matuska off_s = lseek(*fd, off_s, SEEK_DATA); 12016c95142eSMartin Matuska if (off_s == (off_t)-1) { 1202cdf63a70SMartin Matuska if (errno == ENXIO) { 1203cdf63a70SMartin Matuska /* no more hole */ 1204cdf63a70SMartin Matuska if (archive_entry_sparse_count(entry) == 0) { 1205cdf63a70SMartin Matuska /* Potentially a fully-sparse file. */ 1206cdf63a70SMartin Matuska check_fully_sparse = 1; 1207cdf63a70SMartin Matuska } 1208cdf63a70SMartin Matuska break; 1209cdf63a70SMartin Matuska } 12106c95142eSMartin Matuska archive_set_error(&a->archive, errno, 12116c95142eSMartin Matuska "lseek(SEEK_HOLE) failed"); 12126c95142eSMartin Matuska exit_sts = ARCHIVE_FAILED; 12136c95142eSMartin Matuska goto exit_setup_sparse; 12146c95142eSMartin Matuska } 1215fd082e96SMartin Matuska off_e = lseek(*fd, off_s, SEEK_HOLE); 1216fd082e96SMartin Matuska if (off_e == (off_t)-1) { 12176c95142eSMartin Matuska if (errno == ENXIO) { 1218fd082e96SMartin Matuska off_e = lseek(*fd, 0, SEEK_END); 12196c95142eSMartin Matuska if (off_e != (off_t)-1) 12206c95142eSMartin Matuska break;/* no more data */ 12216c95142eSMartin Matuska } 12226c95142eSMartin Matuska archive_set_error(&a->archive, errno, 12236c95142eSMartin Matuska "lseek(SEEK_DATA) failed"); 12246c95142eSMartin Matuska exit_sts = ARCHIVE_FAILED; 12256c95142eSMartin Matuska goto exit_setup_sparse; 12266c95142eSMartin Matuska } 12276c95142eSMartin Matuska if (off_s == 0 && off_e == size) 12286c95142eSMartin Matuska break;/* This is not spase. */ 12296c95142eSMartin Matuska archive_entry_sparse_add_entry(entry, off_s, 12306c95142eSMartin Matuska off_e - off_s); 12316c95142eSMartin Matuska off_s = off_e; 12326c95142eSMartin Matuska } 1233cdf63a70SMartin Matuska 1234cdf63a70SMartin Matuska if (check_fully_sparse) { 1235cdf63a70SMartin Matuska if (lseek(*fd, 0, SEEK_HOLE) == 0 && 1236cdf63a70SMartin Matuska lseek(*fd, 0, SEEK_END) == size) { 1237cdf63a70SMartin Matuska /* Fully sparse file; insert a zero-length "data" entry */ 1238cdf63a70SMartin Matuska archive_entry_sparse_add_entry(entry, 0, 0); 1239cdf63a70SMartin Matuska } 1240cdf63a70SMartin Matuska } 12416c95142eSMartin Matuska exit_setup_sparse: 1242fd082e96SMartin Matuska lseek(*fd, initial_off, SEEK_SET); 12436c95142eSMartin Matuska return (exit_sts); 12446c95142eSMartin Matuska } 12456c95142eSMartin Matuska 12466c95142eSMartin Matuska #else 12476c95142eSMartin Matuska 12486c95142eSMartin Matuska /* 12496c95142eSMartin Matuska * Generic (stub) sparse support. 12506c95142eSMartin Matuska */ 12516c95142eSMartin Matuska static int 12526c95142eSMartin Matuska setup_sparse(struct archive_read_disk *a, 1253fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 12546c95142eSMartin Matuska { 12556c95142eSMartin Matuska (void)a; /* UNUSED */ 12566c95142eSMartin Matuska (void)entry; /* UNUSED */ 12576c95142eSMartin Matuska (void)fd; /* UNUSED */ 12586c95142eSMartin Matuska return (ARCHIVE_OK); 12596c95142eSMartin Matuska } 12606c95142eSMartin Matuska 12616c95142eSMartin Matuska #endif 12626c95142eSMartin Matuska 12636c95142eSMartin Matuska #endif /* !defined(_WIN32) || defined(__CYGWIN__) */ 12646c95142eSMartin Matuska 1265