1caf54c4fSMartin Matuska /*- 2caf54c4fSMartin Matuska * Copyright (c) 2003-2009 Tim Kientzle 3fd082e96SMartin Matuska * Copyright (c) 2010-2012 Michihiro NAKAJIMA 4e9ed7ea4SMartin Matuska * Copyright (c) 2016 Martin Matuska 5caf54c4fSMartin Matuska * All rights reserved. 6caf54c4fSMartin Matuska * 7caf54c4fSMartin Matuska * Redistribution and use in source and binary forms, with or without 8caf54c4fSMartin Matuska * modification, are permitted provided that the following conditions 9caf54c4fSMartin Matuska * are met: 10caf54c4fSMartin Matuska * 1. Redistributions of source code must retain the above copyright 11caf54c4fSMartin Matuska * notice, this list of conditions and the following disclaimer. 12caf54c4fSMartin Matuska * 2. Redistributions in binary form must reproduce the above copyright 13caf54c4fSMartin Matuska * notice, this list of conditions and the following disclaimer in the 14caf54c4fSMartin Matuska * documentation and/or other materials provided with the distribution. 15caf54c4fSMartin Matuska * 16caf54c4fSMartin Matuska * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 17caf54c4fSMartin Matuska * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18caf54c4fSMartin Matuska * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19caf54c4fSMartin Matuska * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 20caf54c4fSMartin Matuska * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21caf54c4fSMartin Matuska * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22caf54c4fSMartin Matuska * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23caf54c4fSMartin Matuska * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24caf54c4fSMartin Matuska * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25caf54c4fSMartin Matuska * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26caf54c4fSMartin Matuska */ 27caf54c4fSMartin Matuska 28caf54c4fSMartin Matuska #include "archive_platform.h" 294657548dSMartin Matuska __FBSDID("$FreeBSD"); 30caf54c4fSMartin Matuska 316c95142eSMartin Matuska /* This is the tree-walking code for POSIX systems. */ 326c95142eSMartin Matuska #if !defined(_WIN32) || defined(__CYGWIN__) 336c95142eSMartin Matuska 34caf54c4fSMartin Matuska #ifdef HAVE_SYS_TYPES_H 35caf54c4fSMartin Matuska #include <sys/types.h> 36caf54c4fSMartin Matuska #endif 37caf54c4fSMartin Matuska #ifdef HAVE_SYS_EXTATTR_H 38caf54c4fSMartin Matuska #include <sys/extattr.h> 39caf54c4fSMartin Matuska #endif 406c95142eSMartin Matuska #ifdef HAVE_SYS_IOCTL_H 416c95142eSMartin Matuska #include <sys/ioctl.h> 426c95142eSMartin Matuska #endif 43caf54c4fSMartin Matuska #ifdef HAVE_SYS_PARAM_H 44caf54c4fSMartin Matuska #include <sys/param.h> 45caf54c4fSMartin Matuska #endif 46caf54c4fSMartin Matuska #ifdef HAVE_SYS_STAT_H 47caf54c4fSMartin Matuska #include <sys/stat.h> 48caf54c4fSMartin Matuska #endif 49acc60b03SMartin Matuska #if defined(HAVE_SYS_XATTR_H) 50caf54c4fSMartin Matuska #include <sys/xattr.h> 51acc60b03SMartin Matuska #elif defined(HAVE_ATTR_XATTR_H) 52acc60b03SMartin Matuska #include <attr/xattr.h> 53caf54c4fSMartin Matuska #endif 546c95142eSMartin Matuska #ifdef HAVE_SYS_EA_H 556c95142eSMartin Matuska #include <sys/ea.h> 566c95142eSMartin Matuska #endif 576c95142eSMartin Matuska #ifdef HAVE_COPYFILE_H 586c95142eSMartin Matuska #include <copyfile.h> 596c95142eSMartin Matuska #endif 60caf54c4fSMartin Matuska #ifdef HAVE_ERRNO_H 61caf54c4fSMartin Matuska #include <errno.h> 62caf54c4fSMartin Matuska #endif 636c95142eSMartin Matuska #ifdef HAVE_FCNTL_H 646c95142eSMartin Matuska #include <fcntl.h> 656c95142eSMartin Matuska #endif 66caf54c4fSMartin Matuska #ifdef HAVE_LIMITS_H 67caf54c4fSMartin Matuska #include <limits.h> 68caf54c4fSMartin Matuska #endif 69fd082e96SMartin Matuska #ifdef HAVE_LINUX_TYPES_H 70fd082e96SMartin Matuska #include <linux/types.h> 71fd082e96SMartin Matuska #endif 726c95142eSMartin Matuska #ifdef HAVE_LINUX_FIEMAP_H 736c95142eSMartin Matuska #include <linux/fiemap.h> 746c95142eSMartin Matuska #endif 756c95142eSMartin Matuska #ifdef HAVE_LINUX_FS_H 766c95142eSMartin Matuska #include <linux/fs.h> 776c95142eSMartin Matuska #endif 786c95142eSMartin Matuska /* 796c95142eSMartin Matuska * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. 806c95142eSMartin Matuska * As the include guards don't agree, the order of include is important. 816c95142eSMartin Matuska */ 826c95142eSMartin Matuska #ifdef HAVE_LINUX_EXT2_FS_H 836c95142eSMartin Matuska #include <linux/ext2_fs.h> /* for Linux file flags */ 846c95142eSMartin Matuska #endif 856c95142eSMartin Matuska #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) 866c95142eSMartin Matuska #include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */ 876c95142eSMartin Matuska #endif 886c95142eSMartin Matuska #ifdef HAVE_PATHS_H 896c95142eSMartin Matuska #include <paths.h> 906c95142eSMartin Matuska #endif 916c95142eSMartin Matuska #ifdef HAVE_UNISTD_H 926c95142eSMartin Matuska #include <unistd.h> 93caf54c4fSMartin Matuska #endif 94caf54c4fSMartin Matuska 95caf54c4fSMartin Matuska #include "archive.h" 96caf54c4fSMartin Matuska #include "archive_entry.h" 97caf54c4fSMartin Matuska #include "archive_private.h" 98caf54c4fSMartin Matuska #include "archive_read_disk_private.h" 99caf54c4fSMartin Matuska 100acc60b03SMartin Matuska #ifndef O_CLOEXEC 101acc60b03SMartin Matuska #define O_CLOEXEC 0 102acc60b03SMartin Matuska #endif 103acc60b03SMartin Matuska 1046c95142eSMartin Matuska static int setup_mac_metadata(struct archive_read_disk *, 105fd082e96SMartin Matuska struct archive_entry *, int *fd); 106caf54c4fSMartin Matuska static int setup_xattrs(struct archive_read_disk *, 107fd082e96SMartin Matuska struct archive_entry *, int *fd); 1086c95142eSMartin Matuska static int setup_sparse(struct archive_read_disk *, 109fd082e96SMartin Matuska struct archive_entry *, int *fd); 110d5d08d29SMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H) 111d5d08d29SMartin Matuska static int setup_sparse_fiemap(struct archive_read_disk *, 112d5d08d29SMartin Matuska struct archive_entry *, int *fd); 113d5d08d29SMartin Matuska #endif 114caf54c4fSMartin Matuska 1154657548dSMartin Matuska #if !ARCHIVE_ACL_SUPPORT 1164657548dSMartin Matuska int 1174657548dSMartin Matuska archive_read_disk_entry_setup_acls(struct archive_read_disk *a, 1184657548dSMartin Matuska struct archive_entry *entry, int *fd) 1194657548dSMartin Matuska { 1204657548dSMartin Matuska (void)a; /* UNUSED */ 1214657548dSMartin Matuska (void)entry; /* UNUSED */ 1224657548dSMartin Matuska (void)fd; /* UNUSED */ 1234657548dSMartin Matuska return (ARCHIVE_OK); 1244657548dSMartin Matuska } 1254657548dSMartin Matuska #endif 1264657548dSMartin Matuska 1274657548dSMartin Matuska /* 1284657548dSMartin Matuska * Enter working directory and return working pathname of archive_entry. 1294657548dSMartin Matuska * If a pointer to an integer is provided and its value is below zero 1304657548dSMartin Matuska * open a file descriptor on this pahtname. 1314657548dSMartin Matuska */ 1324657548dSMartin Matuska const char * 1334657548dSMartin Matuska archive_read_disk_entry_setup_path(struct archive_read_disk *a, 1344657548dSMartin Matuska struct archive_entry *entry, int *fd) 1354657548dSMartin Matuska { 1364657548dSMartin Matuska const char *path; 1374657548dSMartin Matuska 1384657548dSMartin Matuska path = archive_entry_sourcepath(entry); 1394657548dSMartin Matuska 1404657548dSMartin Matuska if (path == NULL || (a->tree != NULL && 1414657548dSMartin Matuska a->tree_enter_working_dir(a->tree) != 0)) 1424657548dSMartin Matuska path = archive_entry_pathname(entry); 1434657548dSMartin Matuska if (path == NULL) { 1444657548dSMartin Matuska archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 1454657548dSMartin Matuska "Couldn't determine path"); 1464657548dSMartin Matuska } else if (fd != NULL && *fd < 0 && a->tree != NULL && 1474657548dSMartin Matuska (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)) { 1484657548dSMartin Matuska *fd = a->open_on_current_dir(a->tree, path, 1494657548dSMartin Matuska O_RDONLY | O_NONBLOCK); 1504657548dSMartin Matuska } 1514657548dSMartin Matuska return (path); 1524657548dSMartin Matuska } 1534657548dSMartin Matuska 154caf54c4fSMartin Matuska int 155caf54c4fSMartin Matuska archive_read_disk_entry_from_file(struct archive *_a, 156caf54c4fSMartin Matuska struct archive_entry *entry, 1576c95142eSMartin Matuska int fd, 1586c95142eSMartin Matuska const struct stat *st) 159caf54c4fSMartin Matuska { 160caf54c4fSMartin Matuska struct archive_read_disk *a = (struct archive_read_disk *)_a; 161caf54c4fSMartin Matuska const char *path, *name; 162caf54c4fSMartin Matuska struct stat s; 163caf54c4fSMartin Matuska int initial_fd = fd; 164caf54c4fSMartin Matuska int r, r1; 165caf54c4fSMartin Matuska 166caf54c4fSMartin Matuska archive_clear_error(_a); 167caf54c4fSMartin Matuska path = archive_entry_sourcepath(entry); 168caf54c4fSMartin Matuska if (path == NULL) 169caf54c4fSMartin Matuska path = archive_entry_pathname(entry); 170caf54c4fSMartin Matuska 1716c95142eSMartin Matuska if (a->tree == NULL) { 172caf54c4fSMartin Matuska if (st == NULL) { 173caf54c4fSMartin Matuska #if HAVE_FSTAT 174caf54c4fSMartin Matuska if (fd >= 0) { 175caf54c4fSMartin Matuska if (fstat(fd, &s) != 0) { 176caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 177caf54c4fSMartin Matuska "Can't fstat"); 178caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 179caf54c4fSMartin Matuska } 180caf54c4fSMartin Matuska } else 181caf54c4fSMartin Matuska #endif 182caf54c4fSMartin Matuska #if HAVE_LSTAT 183caf54c4fSMartin Matuska if (!a->follow_symlinks) { 184caf54c4fSMartin Matuska if (lstat(path, &s) != 0) { 185caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 186caf54c4fSMartin Matuska "Can't lstat %s", path); 187caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 188caf54c4fSMartin Matuska } 189caf54c4fSMartin Matuska } else 190caf54c4fSMartin Matuska #endif 191caf54c4fSMartin Matuska if (stat(path, &s) != 0) { 192caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 1936c95142eSMartin Matuska "Can't stat %s", path); 194caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 195caf54c4fSMartin Matuska } 196caf54c4fSMartin Matuska st = &s; 197caf54c4fSMartin Matuska } 198caf54c4fSMartin Matuska archive_entry_copy_stat(entry, st); 19910ed66fdSMartin Matuska } 20010ed66fdSMartin Matuska 201caf54c4fSMartin Matuska /* Lookup uname/gname */ 202caf54c4fSMartin Matuska name = archive_read_disk_uname(_a, archive_entry_uid(entry)); 203caf54c4fSMartin Matuska if (name != NULL) 204caf54c4fSMartin Matuska archive_entry_copy_uname(entry, name); 205caf54c4fSMartin Matuska name = archive_read_disk_gname(_a, archive_entry_gid(entry)); 206caf54c4fSMartin Matuska if (name != NULL) 207caf54c4fSMartin Matuska archive_entry_copy_gname(entry, name); 208caf54c4fSMartin Matuska 209caf54c4fSMartin Matuska #ifdef HAVE_STRUCT_STAT_ST_FLAGS 210caf54c4fSMartin Matuska /* On FreeBSD, we get flags for free with the stat. */ 211caf54c4fSMartin Matuska /* TODO: Does this belong in copy_stat()? */ 21264287048SMartin Matuska if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0) 213caf54c4fSMartin Matuska archive_entry_set_fflags(entry, st->st_flags, 0); 214caf54c4fSMartin Matuska #endif 215caf54c4fSMartin Matuska 21664287048SMartin Matuska #if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ 21764287048SMartin Matuska (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) 2186c95142eSMartin Matuska /* Linux requires an extra ioctl to pull the flags. Although 2196c95142eSMartin Matuska * this is an extra step, it has a nice side-effect: We get an 2206c95142eSMartin Matuska * open file descriptor which we can use in the subsequent lookups. */ 22164287048SMartin Matuska if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && 22264287048SMartin Matuska (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { 223fd082e96SMartin Matuska if (fd < 0) { 224fd082e96SMartin Matuska if (a->tree != NULL) 225fd082e96SMartin Matuska fd = a->open_on_current_dir(a->tree, path, 226acc60b03SMartin Matuska O_RDONLY | O_NONBLOCK | O_CLOEXEC); 227fd082e96SMartin Matuska else 228acc60b03SMartin Matuska fd = open(path, O_RDONLY | O_NONBLOCK | 229acc60b03SMartin Matuska O_CLOEXEC); 230acc60b03SMartin Matuska __archive_ensure_cloexec_flag(fd); 231fd082e96SMartin Matuska } 2326c95142eSMartin Matuska if (fd >= 0) { 233acc60b03SMartin Matuska int stflags; 23464287048SMartin Matuska r = ioctl(fd, 23564287048SMartin Matuska #if defined(FS_IOC_GETFLAGS) 23664287048SMartin Matuska FS_IOC_GETFLAGS, 23764287048SMartin Matuska #else 23864287048SMartin Matuska EXT2_IOC_GETFLAGS, 23964287048SMartin Matuska #endif 24064287048SMartin Matuska &stflags); 2416c95142eSMartin Matuska if (r == 0 && stflags != 0) 2426c95142eSMartin Matuska archive_entry_set_fflags(entry, stflags, 0); 2436c95142eSMartin Matuska } 2446c95142eSMartin Matuska } 2456c95142eSMartin Matuska #endif 2466c95142eSMartin Matuska 2476c95142eSMartin Matuska #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) 248caf54c4fSMartin Matuska if (S_ISLNK(st->st_mode)) { 2496c95142eSMartin Matuska size_t linkbuffer_len = st->st_size + 1; 2506c95142eSMartin Matuska char *linkbuffer; 2516c95142eSMartin Matuska int lnklen; 2526c95142eSMartin Matuska 2536c95142eSMartin Matuska linkbuffer = malloc(linkbuffer_len); 2546c95142eSMartin Matuska if (linkbuffer == NULL) { 2556c95142eSMartin Matuska archive_set_error(&a->archive, ENOMEM, 2566c95142eSMartin Matuska "Couldn't read link data"); 2576c95142eSMartin Matuska return (ARCHIVE_FAILED); 2586c95142eSMartin Matuska } 259fd082e96SMartin Matuska if (a->tree != NULL) { 2606c95142eSMartin Matuska #ifdef HAVE_READLINKAT 261fd082e96SMartin Matuska lnklen = readlinkat(a->tree_current_dir_fd(a->tree), 262fd082e96SMartin Matuska path, linkbuffer, linkbuffer_len); 263fd082e96SMartin Matuska #else 264fd082e96SMartin Matuska if (a->tree_enter_working_dir(a->tree) != 0) { 265fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 266fd082e96SMartin Matuska "Couldn't read link data"); 267fd082e96SMartin Matuska free(linkbuffer); 268fd082e96SMartin Matuska return (ARCHIVE_FAILED); 269fd082e96SMartin Matuska } 270fd082e96SMartin Matuska lnklen = readlink(path, linkbuffer, linkbuffer_len); 2716c95142eSMartin Matuska #endif /* HAVE_READLINKAT */ 272fd082e96SMartin Matuska } else 2736c95142eSMartin Matuska lnklen = readlink(path, linkbuffer, linkbuffer_len); 274caf54c4fSMartin Matuska if (lnklen < 0) { 275caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 276caf54c4fSMartin Matuska "Couldn't read link data"); 2776c95142eSMartin Matuska free(linkbuffer); 278caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 279caf54c4fSMartin Matuska } 280caf54c4fSMartin Matuska linkbuffer[lnklen] = 0; 281caf54c4fSMartin Matuska archive_entry_set_symlink(entry, linkbuffer); 2826c95142eSMartin Matuska free(linkbuffer); 283caf54c4fSMartin Matuska } 2846c95142eSMartin Matuska #endif /* HAVE_READLINK || HAVE_READLINKAT */ 285caf54c4fSMartin Matuska 28664287048SMartin Matuska r = 0; 28764287048SMartin Matuska if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0) 2884657548dSMartin Matuska r = archive_read_disk_entry_setup_acls(a, entry, &fd); 28964287048SMartin Matuska if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) { 290fd082e96SMartin Matuska r1 = setup_xattrs(a, entry, &fd); 291caf54c4fSMartin Matuska if (r1 < r) 292caf54c4fSMartin Matuska r = r1; 293cdf63a70SMartin Matuska } 29464287048SMartin Matuska if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) { 295fd082e96SMartin Matuska r1 = setup_mac_metadata(a, entry, &fd); 2966c95142eSMartin Matuska if (r1 < r) 2976c95142eSMartin Matuska r = r1; 298fd082e96SMartin Matuska } 299fd082e96SMartin Matuska r1 = setup_sparse(a, entry, &fd); 3006c95142eSMartin Matuska if (r1 < r) 3016c95142eSMartin Matuska r = r1; 3026c95142eSMartin Matuska 303caf54c4fSMartin Matuska /* If we opened the file earlier in this function, close it. */ 304caf54c4fSMartin Matuska if (initial_fd != fd) 305caf54c4fSMartin Matuska close(fd); 306caf54c4fSMartin Matuska return (r); 307caf54c4fSMartin Matuska } 308caf54c4fSMartin Matuska 3096c95142eSMartin Matuska #if defined(__APPLE__) && defined(HAVE_COPYFILE_H) 3106c95142eSMartin Matuska /* 3116c95142eSMartin Matuska * The Mac OS "copyfile()" API copies the extended metadata for a 3126c95142eSMartin Matuska * file into a separate file in AppleDouble format (see RFC 1740). 3136c95142eSMartin Matuska * 3146c95142eSMartin Matuska * Mac OS tar and cpio implementations store this extended 3156c95142eSMartin Matuska * metadata as a separate entry just before the regular entry 3166c95142eSMartin Matuska * with a "._" prefix added to the filename. 3176c95142eSMartin Matuska * 3186c95142eSMartin Matuska * Note that this is currently done unconditionally; the tar program has 3196c95142eSMartin Matuska * an option to discard this information before the archive is written. 3206c95142eSMartin Matuska * 3216c95142eSMartin Matuska * TODO: If there's a failure, report it and return ARCHIVE_WARN. 3226c95142eSMartin Matuska */ 3236c95142eSMartin Matuska static int 3246c95142eSMartin Matuska setup_mac_metadata(struct archive_read_disk *a, 325fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 3266c95142eSMartin Matuska { 3276c95142eSMartin Matuska int tempfd = -1; 3286c95142eSMartin Matuska int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR; 3296c95142eSMartin Matuska struct stat copyfile_stat; 3306c95142eSMartin Matuska int ret = ARCHIVE_OK; 331acc60b03SMartin Matuska void *buff = NULL; 3326c95142eSMartin Matuska int have_attrs; 333acc60b03SMartin Matuska const char *name, *tempdir; 334acc60b03SMartin Matuska struct archive_string tempfile; 3356c95142eSMartin Matuska 336fd082e96SMartin Matuska (void)fd; /* UNUSED */ 3374657548dSMartin Matuska 3384657548dSMartin Matuska name = archive_read_disk_entry_setup_path(a, entry, NULL); 3396c95142eSMartin Matuska if (name == NULL) 3406c95142eSMartin Matuska return (ARCHIVE_WARN); 3416c95142eSMartin Matuska 3426c95142eSMartin Matuska /* Short-circuit if there's nothing to do. */ 3436c95142eSMartin Matuska have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK); 3446c95142eSMartin Matuska if (have_attrs == -1) { 3456c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3466c95142eSMartin Matuska "Could not check extended attributes"); 3476c95142eSMartin Matuska return (ARCHIVE_WARN); 3486c95142eSMartin Matuska } 3496c95142eSMartin Matuska if (have_attrs == 0) 3506c95142eSMartin Matuska return (ARCHIVE_OK); 3516c95142eSMartin Matuska 3526c95142eSMartin Matuska tempdir = NULL; 3536c95142eSMartin Matuska if (issetugid() == 0) 3546c95142eSMartin Matuska tempdir = getenv("TMPDIR"); 3556c95142eSMartin Matuska if (tempdir == NULL) 3566c95142eSMartin Matuska tempdir = _PATH_TMP; 357acc60b03SMartin Matuska archive_string_init(&tempfile); 358acc60b03SMartin Matuska archive_strcpy(&tempfile, tempdir); 359acc60b03SMartin Matuska archive_strcat(&tempfile, "tar.md.XXXXXX"); 360acc60b03SMartin Matuska tempfd = mkstemp(tempfile.s); 361acc60b03SMartin Matuska if (tempfd < 0) { 362acc60b03SMartin Matuska archive_set_error(&a->archive, errno, 363acc60b03SMartin Matuska "Could not open extended attribute file"); 364acc60b03SMartin Matuska ret = ARCHIVE_WARN; 365acc60b03SMartin Matuska goto cleanup; 366acc60b03SMartin Matuska } 367acc60b03SMartin Matuska __archive_ensure_cloexec_flag(tempfd); 3686c95142eSMartin Matuska 3696c95142eSMartin Matuska /* XXX I wish copyfile() could pack directly to a memory 3706c95142eSMartin Matuska * buffer; that would avoid the temp file here. For that 3716c95142eSMartin Matuska * matter, it would be nice if fcopyfile() actually worked, 3726c95142eSMartin Matuska * that would reduce the many open/close races here. */ 373acc60b03SMartin Matuska if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) { 3746c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3756c95142eSMartin Matuska "Could not pack extended attributes"); 3766c95142eSMartin Matuska ret = ARCHIVE_WARN; 3776c95142eSMartin Matuska goto cleanup; 3786c95142eSMartin Matuska } 3796c95142eSMartin Matuska if (fstat(tempfd, ©file_stat)) { 3806c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3816c95142eSMartin Matuska "Could not check size of extended attributes"); 3826c95142eSMartin Matuska ret = ARCHIVE_WARN; 3836c95142eSMartin Matuska goto cleanup; 3846c95142eSMartin Matuska } 3856c95142eSMartin Matuska buff = malloc(copyfile_stat.st_size); 3866c95142eSMartin Matuska if (buff == NULL) { 3876c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3886c95142eSMartin Matuska "Could not allocate memory for extended attributes"); 3896c95142eSMartin Matuska ret = ARCHIVE_WARN; 3906c95142eSMartin Matuska goto cleanup; 3916c95142eSMartin Matuska } 3926c95142eSMartin Matuska if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) { 3936c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3946c95142eSMartin Matuska "Could not read extended attributes into memory"); 3956c95142eSMartin Matuska ret = ARCHIVE_WARN; 3966c95142eSMartin Matuska goto cleanup; 3976c95142eSMartin Matuska } 3986c95142eSMartin Matuska archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size); 3996c95142eSMartin Matuska 4006c95142eSMartin Matuska cleanup: 401acc60b03SMartin Matuska if (tempfd >= 0) { 4026c95142eSMartin Matuska close(tempfd); 403acc60b03SMartin Matuska unlink(tempfile.s); 404acc60b03SMartin Matuska } 405acc60b03SMartin Matuska archive_string_free(&tempfile); 406acc60b03SMartin Matuska free(buff); 4076c95142eSMartin Matuska return (ret); 4086c95142eSMartin Matuska } 4096c95142eSMartin Matuska 4106c95142eSMartin Matuska #else 4116c95142eSMartin Matuska 4126c95142eSMartin Matuska /* 4136c95142eSMartin Matuska * Stub implementation for non-Mac systems. 4146c95142eSMartin Matuska */ 4156c95142eSMartin Matuska static int 4166c95142eSMartin Matuska setup_mac_metadata(struct archive_read_disk *a, 417fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 4186c95142eSMartin Matuska { 4196c95142eSMartin Matuska (void)a; /* UNUSED */ 4206c95142eSMartin Matuska (void)entry; /* UNUSED */ 4216c95142eSMartin Matuska (void)fd; /* UNUSED */ 4226c95142eSMartin Matuska return (ARCHIVE_OK); 4236c95142eSMartin Matuska } 4246c95142eSMartin Matuska #endif 4256c95142eSMartin Matuska 426e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX 427caf54c4fSMartin Matuska 428caf54c4fSMartin Matuska /* 429e46d4714SMartin Matuska * Linux, Darwin and AIX extended attribute support. 430caf54c4fSMartin Matuska * 431caf54c4fSMartin Matuska * TODO: By using a stack-allocated buffer for the first 432caf54c4fSMartin Matuska * call to getxattr(), we might be able to avoid the second 433caf54c4fSMartin Matuska * call entirely. We only need the second call if the 434caf54c4fSMartin Matuska * stack-allocated buffer is too small. But a modest buffer 435caf54c4fSMartin Matuska * of 1024 bytes or so will often be big enough. Same applies 436caf54c4fSMartin Matuska * to listxattr(). 437caf54c4fSMartin Matuska */ 438caf54c4fSMartin Matuska 439caf54c4fSMartin Matuska 440caf54c4fSMartin Matuska static int 441caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, 44264287048SMartin Matuska struct archive_entry *entry, const char *name, int fd, const char *accpath) 443caf54c4fSMartin Matuska { 444caf54c4fSMartin Matuska ssize_t size; 445caf54c4fSMartin Matuska void *value = NULL; 446caf54c4fSMartin Matuska 447e46d4714SMartin Matuska 448e46d4714SMartin Matuska if (fd >= 0) { 449e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 4506c95142eSMartin Matuska size = fgetxattr(fd, name, NULL, 0); 451e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 452e46d4714SMartin Matuska size = fgetxattr(fd, name, NULL, 0, 0, 0); 453e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 4546c95142eSMartin Matuska size = fgetea(fd, name, NULL, 0); 455e46d4714SMartin Matuska #endif 456e46d4714SMartin Matuska } else if (!a->follow_symlinks) { 457e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 458e46d4714SMartin Matuska size = lgetxattr(accpath, name, NULL, 0); 459e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 460e46d4714SMartin Matuska size = getxattr(accpath, name, NULL, 0, 0, XATTR_NOFOLLOW); 461e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 4626c95142eSMartin Matuska size = lgetea(accpath, name, NULL, 0); 463e46d4714SMartin Matuska #endif 464e46d4714SMartin Matuska } else { 465e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 466e46d4714SMartin Matuska size = getxattr(accpath, name, NULL, 0); 467e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 468e46d4714SMartin Matuska size = getxattr(accpath, name, NULL, 0, 0, 0); 469e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 4706c95142eSMartin Matuska size = getea(accpath, name, NULL, 0); 4716c95142eSMartin Matuska #endif 472e46d4714SMartin Matuska } 473caf54c4fSMartin Matuska 474caf54c4fSMartin Matuska if (size == -1) { 475caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 476caf54c4fSMartin Matuska "Couldn't query extended attribute"); 477caf54c4fSMartin Matuska return (ARCHIVE_WARN); 478caf54c4fSMartin Matuska } 479caf54c4fSMartin Matuska 480caf54c4fSMartin Matuska if (size > 0 && (value = malloc(size)) == NULL) { 481caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 482caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 483caf54c4fSMartin Matuska } 484caf54c4fSMartin Matuska 485e46d4714SMartin Matuska 486e46d4714SMartin Matuska if (fd >= 0) { 487e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 4886c95142eSMartin Matuska size = fgetxattr(fd, name, value, size); 489e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 490e46d4714SMartin Matuska size = fgetxattr(fd, name, value, size, 0, 0); 491e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 4926c95142eSMartin Matuska size = fgetea(fd, name, value, size); 493e46d4714SMartin Matuska #endif 494e46d4714SMartin Matuska } else if (!a->follow_symlinks) { 495e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 496e46d4714SMartin Matuska size = lgetxattr(accpath, name, value, size); 497e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 498e46d4714SMartin Matuska size = getxattr(accpath, name, value, size, 0, XATTR_NOFOLLOW); 499e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 5006c95142eSMartin Matuska size = lgetea(accpath, name, value, size); 501e46d4714SMartin Matuska #endif 502e46d4714SMartin Matuska } else { 503e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 504e46d4714SMartin Matuska size = getxattr(accpath, name, value, size); 505e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 506e46d4714SMartin Matuska size = getxattr(accpath, name, value, size, 0, 0); 507e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 5086c95142eSMartin Matuska size = getea(accpath, name, value, size); 5096c95142eSMartin Matuska #endif 510e46d4714SMartin Matuska } 511caf54c4fSMartin Matuska 512caf54c4fSMartin Matuska if (size == -1) { 513caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 514caf54c4fSMartin Matuska "Couldn't read extended attribute"); 515caf54c4fSMartin Matuska return (ARCHIVE_WARN); 516caf54c4fSMartin Matuska } 517caf54c4fSMartin Matuska 518caf54c4fSMartin Matuska archive_entry_xattr_add_entry(entry, name, value, size); 519caf54c4fSMartin Matuska 520caf54c4fSMartin Matuska free(value); 521caf54c4fSMartin Matuska return (ARCHIVE_OK); 522caf54c4fSMartin Matuska } 523caf54c4fSMartin Matuska 524caf54c4fSMartin Matuska static int 525caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a, 526fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 527caf54c4fSMartin Matuska { 528caf54c4fSMartin Matuska char *list, *p; 529caf54c4fSMartin Matuska const char *path; 530caf54c4fSMartin Matuska ssize_t list_size; 531caf54c4fSMartin Matuska 53264287048SMartin Matuska path = NULL; 533caf54c4fSMartin Matuska 534fd082e96SMartin Matuska if (*fd < 0) { 5354657548dSMartin Matuska path = archive_read_disk_entry_setup_path(a, entry, fd); 5364657548dSMartin Matuska if (path == NULL) 53764287048SMartin Matuska return (ARCHIVE_WARN); 538fd082e96SMartin Matuska } 539fd082e96SMartin Matuska 540e46d4714SMartin Matuska if (*fd >= 0) { 541e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 542fd082e96SMartin Matuska list_size = flistxattr(*fd, NULL, 0); 543e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 544e46d4714SMartin Matuska list_size = flistxattr(*fd, NULL, 0, 0); 545e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 546fd082e96SMartin Matuska list_size = flistea(*fd, NULL, 0); 547e46d4714SMartin Matuska #endif 548e46d4714SMartin Matuska } else if (!a->follow_symlinks) { 549e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 550e46d4714SMartin Matuska list_size = llistxattr(path, NULL, 0); 551e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 552e46d4714SMartin Matuska list_size = listxattr(path, NULL, 0, XATTR_NOFOLLOW); 553e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 5546c95142eSMartin Matuska list_size = llistea(path, NULL, 0); 555e46d4714SMartin Matuska #endif 556e46d4714SMartin Matuska } else { 557e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 558e46d4714SMartin Matuska list_size = listxattr(path, NULL, 0); 559e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 560e46d4714SMartin Matuska list_size = listxattr(path, NULL, 0, 0); 561e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 5626c95142eSMartin Matuska list_size = listea(path, NULL, 0); 5636c95142eSMartin Matuska #endif 564e46d4714SMartin Matuska } 565caf54c4fSMartin Matuska 566caf54c4fSMartin Matuska if (list_size == -1) { 5676c95142eSMartin Matuska if (errno == ENOTSUP || errno == ENOSYS) 568caf54c4fSMartin Matuska return (ARCHIVE_OK); 569caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 570caf54c4fSMartin Matuska "Couldn't list extended attributes"); 571caf54c4fSMartin Matuska return (ARCHIVE_WARN); 572caf54c4fSMartin Matuska } 573caf54c4fSMartin Matuska 574caf54c4fSMartin Matuska if (list_size == 0) 575caf54c4fSMartin Matuska return (ARCHIVE_OK); 576caf54c4fSMartin Matuska 577caf54c4fSMartin Matuska if ((list = malloc(list_size)) == NULL) { 578caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 579caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 580caf54c4fSMartin Matuska } 581caf54c4fSMartin Matuska 582e46d4714SMartin Matuska if (*fd >= 0) { 583e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 584fd082e96SMartin Matuska list_size = flistxattr(*fd, list, list_size); 585e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 586e46d4714SMartin Matuska list_size = flistxattr(*fd, list, list_size, 0); 587e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 588fd082e96SMartin Matuska list_size = flistea(*fd, list, list_size); 589e46d4714SMartin Matuska #endif 590e46d4714SMartin Matuska } else if (!a->follow_symlinks) { 591e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 592e46d4714SMartin Matuska list_size = llistxattr(path, list, list_size); 593e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 594e46d4714SMartin Matuska list_size = listxattr(path, list, list_size, XATTR_NOFOLLOW); 595e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 5966c95142eSMartin Matuska list_size = llistea(path, list, list_size); 597e46d4714SMartin Matuska #endif 598e46d4714SMartin Matuska } else { 599e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX 600e46d4714SMartin Matuska list_size = listxattr(path, list, list_size); 601e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN 602e46d4714SMartin Matuska list_size = listxattr(path, list, list_size, 0); 603e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX 6046c95142eSMartin Matuska list_size = listea(path, list, list_size); 6056c95142eSMartin Matuska #endif 606e46d4714SMartin Matuska } 607caf54c4fSMartin Matuska 608caf54c4fSMartin Matuska if (list_size == -1) { 609caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 610caf54c4fSMartin Matuska "Couldn't retrieve extended attributes"); 611caf54c4fSMartin Matuska free(list); 612caf54c4fSMartin Matuska return (ARCHIVE_WARN); 613caf54c4fSMartin Matuska } 614caf54c4fSMartin Matuska 615caf54c4fSMartin Matuska for (p = list; (p - list) < list_size; p += strlen(p) + 1) { 61643f9e382SMartin Matuska #if ARCHIVE_XATTR_LINUX 61743f9e382SMartin Matuska /* Linux: skip POSIX.1e ACL extended attributes */ 61843f9e382SMartin Matuska if (strncmp(p, "system.", 7) == 0 && 61943f9e382SMartin Matuska (strcmp(p + 7, "posix_acl_access") == 0 || 62043f9e382SMartin Matuska strcmp(p + 7, "posix_acl_default") == 0)) 621caf54c4fSMartin Matuska continue; 62243f9e382SMartin Matuska if (strncmp(p, "trusted.SGI_", 12) == 0 && 62343f9e382SMartin Matuska (strcmp(p + 12, "ACL_DEFAULT") == 0 || 62443f9e382SMartin Matuska strcmp(p + 12, "ACL_FILE") == 0)) 62543f9e382SMartin Matuska continue; 62643f9e382SMartin Matuska 62743f9e382SMartin Matuska /* Linux: xfsroot namespace is obsolete and unsupported */ 62843f9e382SMartin Matuska if (strncmp(p, "xfsroot.", 8) == 0) 62943f9e382SMartin Matuska continue; 63043f9e382SMartin Matuska #endif 63164287048SMartin Matuska setup_xattr(a, entry, p, *fd, path); 632caf54c4fSMartin Matuska } 633caf54c4fSMartin Matuska 634caf54c4fSMartin Matuska free(list); 635caf54c4fSMartin Matuska return (ARCHIVE_OK); 636caf54c4fSMartin Matuska } 637caf54c4fSMartin Matuska 638e46d4714SMartin Matuska #elif ARCHIVE_XATTR_FREEBSD 639caf54c4fSMartin Matuska 640caf54c4fSMartin Matuska /* 641caf54c4fSMartin Matuska * FreeBSD extattr interface. 642caf54c4fSMartin Matuska */ 643caf54c4fSMartin Matuska 644caf54c4fSMartin Matuska /* TODO: Implement this. Follow the Linux model above, but 645caf54c4fSMartin Matuska * with FreeBSD-specific system calls, of course. Be careful 646caf54c4fSMartin Matuska * to not include the system extattrs that hold ACLs; we handle 647caf54c4fSMartin Matuska * those separately. 648caf54c4fSMartin Matuska */ 649caf54c4fSMartin Matuska static int 650caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, 65164287048SMartin Matuska int namespace, const char *name, const char *fullname, int fd, 65264287048SMartin Matuska const char *path); 653caf54c4fSMartin Matuska 654caf54c4fSMartin Matuska static int 655caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, 65664287048SMartin Matuska int namespace, const char *name, const char *fullname, int fd, 65764287048SMartin Matuska const char *accpath) 658caf54c4fSMartin Matuska { 659caf54c4fSMartin Matuska ssize_t size; 660caf54c4fSMartin Matuska void *value = NULL; 661caf54c4fSMartin Matuska 6626c95142eSMartin Matuska if (fd >= 0) 6636c95142eSMartin Matuska size = extattr_get_fd(fd, namespace, name, NULL, 0); 6646c95142eSMartin Matuska else if (!a->follow_symlinks) 665caf54c4fSMartin Matuska size = extattr_get_link(accpath, namespace, name, NULL, 0); 666caf54c4fSMartin Matuska else 667caf54c4fSMartin Matuska size = extattr_get_file(accpath, namespace, name, NULL, 0); 668caf54c4fSMartin Matuska 669caf54c4fSMartin Matuska if (size == -1) { 670caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 671caf54c4fSMartin Matuska "Couldn't query extended attribute"); 672caf54c4fSMartin Matuska return (ARCHIVE_WARN); 673caf54c4fSMartin Matuska } 674caf54c4fSMartin Matuska 675caf54c4fSMartin Matuska if (size > 0 && (value = malloc(size)) == NULL) { 676caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 677caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 678caf54c4fSMartin Matuska } 679caf54c4fSMartin Matuska 6806c95142eSMartin Matuska if (fd >= 0) 6816c95142eSMartin Matuska size = extattr_get_fd(fd, namespace, name, value, size); 6826c95142eSMartin Matuska else if (!a->follow_symlinks) 683caf54c4fSMartin Matuska size = extattr_get_link(accpath, namespace, name, value, size); 684caf54c4fSMartin Matuska else 685caf54c4fSMartin Matuska size = extattr_get_file(accpath, namespace, name, value, size); 686caf54c4fSMartin Matuska 687caf54c4fSMartin Matuska if (size == -1) { 688fd082e96SMartin Matuska free(value); 689caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 690caf54c4fSMartin Matuska "Couldn't read extended attribute"); 691caf54c4fSMartin Matuska return (ARCHIVE_WARN); 692caf54c4fSMartin Matuska } 693caf54c4fSMartin Matuska 694caf54c4fSMartin Matuska archive_entry_xattr_add_entry(entry, fullname, value, size); 695caf54c4fSMartin Matuska 696caf54c4fSMartin Matuska free(value); 697caf54c4fSMartin Matuska return (ARCHIVE_OK); 698caf54c4fSMartin Matuska } 699caf54c4fSMartin Matuska 700caf54c4fSMartin Matuska static int 701caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a, 702fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 703caf54c4fSMartin Matuska { 704caf54c4fSMartin Matuska char buff[512]; 705caf54c4fSMartin Matuska char *list, *p; 706caf54c4fSMartin Matuska ssize_t list_size; 707caf54c4fSMartin Matuska const char *path; 708caf54c4fSMartin Matuska int namespace = EXTATTR_NAMESPACE_USER; 709caf54c4fSMartin Matuska 71064287048SMartin Matuska path = NULL; 711caf54c4fSMartin Matuska 712fd082e96SMartin Matuska if (*fd < 0) { 7134657548dSMartin Matuska path = archive_read_disk_entry_setup_path(a, entry, fd); 7144657548dSMartin Matuska if (path == NULL) 71564287048SMartin Matuska return (ARCHIVE_WARN); 716fd082e96SMartin Matuska } 717fd082e96SMartin Matuska 718fd082e96SMartin Matuska if (*fd >= 0) 719fd082e96SMartin Matuska list_size = extattr_list_fd(*fd, namespace, NULL, 0); 7206c95142eSMartin Matuska else if (!a->follow_symlinks) 721caf54c4fSMartin Matuska list_size = extattr_list_link(path, namespace, NULL, 0); 722caf54c4fSMartin Matuska else 723caf54c4fSMartin Matuska list_size = extattr_list_file(path, namespace, NULL, 0); 724caf54c4fSMartin Matuska 725caf54c4fSMartin Matuska if (list_size == -1 && errno == EOPNOTSUPP) 726caf54c4fSMartin Matuska return (ARCHIVE_OK); 727caf54c4fSMartin Matuska if (list_size == -1) { 728caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 729caf54c4fSMartin Matuska "Couldn't list extended attributes"); 730caf54c4fSMartin Matuska return (ARCHIVE_WARN); 731caf54c4fSMartin Matuska } 732caf54c4fSMartin Matuska 733caf54c4fSMartin Matuska if (list_size == 0) 734caf54c4fSMartin Matuska return (ARCHIVE_OK); 735caf54c4fSMartin Matuska 736caf54c4fSMartin Matuska if ((list = malloc(list_size)) == NULL) { 737caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 738caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 739caf54c4fSMartin Matuska } 740caf54c4fSMartin Matuska 741fd082e96SMartin Matuska if (*fd >= 0) 742fd082e96SMartin Matuska list_size = extattr_list_fd(*fd, namespace, list, list_size); 7436c95142eSMartin Matuska else if (!a->follow_symlinks) 744caf54c4fSMartin Matuska list_size = extattr_list_link(path, namespace, list, list_size); 745caf54c4fSMartin Matuska else 746caf54c4fSMartin Matuska list_size = extattr_list_file(path, namespace, list, list_size); 747caf54c4fSMartin Matuska 748caf54c4fSMartin Matuska if (list_size == -1) { 749caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 750caf54c4fSMartin Matuska "Couldn't retrieve extended attributes"); 751caf54c4fSMartin Matuska free(list); 752caf54c4fSMartin Matuska return (ARCHIVE_WARN); 753caf54c4fSMartin Matuska } 754caf54c4fSMartin Matuska 755caf54c4fSMartin Matuska p = list; 756caf54c4fSMartin Matuska while ((p - list) < list_size) { 757caf54c4fSMartin Matuska size_t len = 255 & (int)*p; 758caf54c4fSMartin Matuska char *name; 759caf54c4fSMartin Matuska 760caf54c4fSMartin Matuska strcpy(buff, "user."); 761caf54c4fSMartin Matuska name = buff + strlen(buff); 762caf54c4fSMartin Matuska memcpy(name, p + 1, len); 763caf54c4fSMartin Matuska name[len] = '\0'; 76464287048SMartin Matuska setup_xattr(a, entry, namespace, name, buff, *fd, path); 765caf54c4fSMartin Matuska p += 1 + len; 766caf54c4fSMartin Matuska } 767caf54c4fSMartin Matuska 768caf54c4fSMartin Matuska free(list); 769caf54c4fSMartin Matuska return (ARCHIVE_OK); 770caf54c4fSMartin Matuska } 771caf54c4fSMartin Matuska 772caf54c4fSMartin Matuska #else 773caf54c4fSMartin Matuska 774caf54c4fSMartin Matuska /* 775caf54c4fSMartin Matuska * Generic (stub) extended attribute support. 776caf54c4fSMartin Matuska */ 777caf54c4fSMartin Matuska static int 778caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a, 779fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 780caf54c4fSMartin Matuska { 781caf54c4fSMartin Matuska (void)a; /* UNUSED */ 782caf54c4fSMartin Matuska (void)entry; /* UNUSED */ 783caf54c4fSMartin Matuska (void)fd; /* UNUSED */ 784caf54c4fSMartin Matuska return (ARCHIVE_OK); 785caf54c4fSMartin Matuska } 786caf54c4fSMartin Matuska 787caf54c4fSMartin Matuska #endif 7886c95142eSMartin Matuska 7896c95142eSMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H) 7906c95142eSMartin Matuska 7916c95142eSMartin Matuska /* 792d5d08d29SMartin Matuska * Linux FIEMAP sparse interface. 7936c95142eSMartin Matuska * 7946c95142eSMartin Matuska * The FIEMAP ioctl returns an "extent" for each physical allocation 7956c95142eSMartin Matuska * on disk. We need to process those to generate a more compact list 7966c95142eSMartin Matuska * of logical file blocks. We also need to be very careful to use 7976c95142eSMartin Matuska * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes 7986c95142eSMartin Matuska * does not report allocations for newly-written data that hasn't 7996c95142eSMartin Matuska * been synced to disk. 8006c95142eSMartin Matuska * 8016c95142eSMartin Matuska * It's important to return a minimal sparse file list because we want 8026c95142eSMartin Matuska * to not trigger sparse file extensions if we don't have to, since 8036c95142eSMartin Matuska * not all readers support them. 8046c95142eSMartin Matuska */ 8056c95142eSMartin Matuska 8066c95142eSMartin Matuska static int 807d5d08d29SMartin Matuska setup_sparse_fiemap(struct archive_read_disk *a, 808fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 8096c95142eSMartin Matuska { 8106c95142eSMartin Matuska char buff[4096]; 8116c95142eSMartin Matuska struct fiemap *fm; 8126c95142eSMartin Matuska struct fiemap_extent *fe; 8136c95142eSMartin Matuska int64_t size; 814cdf63a70SMartin Matuska int count, do_fiemap, iters; 8156c95142eSMartin Matuska int exit_sts = ARCHIVE_OK; 8164657548dSMartin Matuska const char *path; 8176c95142eSMartin Matuska 8186c95142eSMartin Matuska if (archive_entry_filetype(entry) != AE_IFREG 8196c95142eSMartin Matuska || archive_entry_size(entry) <= 0 8206c95142eSMartin Matuska || archive_entry_hardlink(entry) != NULL) 8216c95142eSMartin Matuska return (ARCHIVE_OK); 8226c95142eSMartin Matuska 823fd082e96SMartin Matuska if (*fd < 0) { 8244657548dSMartin Matuska path = archive_read_disk_entry_setup_path(a, entry, NULL); 8256c95142eSMartin Matuska if (path == NULL) 8264657548dSMartin Matuska return (ARCHIVE_FAILED); 8274657548dSMartin Matuska 828fd082e96SMartin Matuska if (a->tree != NULL) 829fd082e96SMartin Matuska *fd = a->open_on_current_dir(a->tree, path, 830acc60b03SMartin Matuska O_RDONLY | O_NONBLOCK | O_CLOEXEC); 831fd082e96SMartin Matuska else 832acc60b03SMartin Matuska *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); 833fd082e96SMartin Matuska if (*fd < 0) { 8346c95142eSMartin Matuska archive_set_error(&a->archive, errno, 8356c95142eSMartin Matuska "Can't open `%s'", path); 8366c95142eSMartin Matuska return (ARCHIVE_FAILED); 8376c95142eSMartin Matuska } 838acc60b03SMartin Matuska __archive_ensure_cloexec_flag(*fd); 8396c95142eSMartin Matuska } 8406c95142eSMartin Matuska 841acc60b03SMartin Matuska /* Initialize buffer to avoid the error valgrind complains about. */ 842acc60b03SMartin Matuska memset(buff, 0, sizeof(buff)); 8436c95142eSMartin Matuska count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe); 8446c95142eSMartin Matuska fm = (struct fiemap *)buff; 8456c95142eSMartin Matuska fm->fm_start = 0; 8466c95142eSMartin Matuska fm->fm_length = ~0ULL;; 8476c95142eSMartin Matuska fm->fm_flags = FIEMAP_FLAG_SYNC; 8486c95142eSMartin Matuska fm->fm_extent_count = count; 8496c95142eSMartin Matuska do_fiemap = 1; 8506c95142eSMartin Matuska size = archive_entry_size(entry); 851cdf63a70SMartin Matuska for (iters = 0; ; ++iters) { 8526c95142eSMartin Matuska int i, r; 8536c95142eSMartin Matuska 854fd082e96SMartin Matuska r = ioctl(*fd, FS_IOC_FIEMAP, fm); 8556c95142eSMartin Matuska if (r < 0) { 856fd082e96SMartin Matuska /* When something error happens, it is better we 857fd082e96SMartin Matuska * should return ARCHIVE_OK because an earlier 858a2e802b7SMartin Matuska * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */ 859d5d08d29SMartin Matuska goto exit_setup_sparse_fiemap; 8606c95142eSMartin Matuska } 861cdf63a70SMartin Matuska if (fm->fm_mapped_extents == 0) { 862cdf63a70SMartin Matuska if (iters == 0) { 863cdf63a70SMartin Matuska /* Fully sparse file; insert a zero-length "data" entry */ 864cdf63a70SMartin Matuska archive_entry_sparse_add_entry(entry, 0, 0); 865cdf63a70SMartin Matuska } 8666c95142eSMartin Matuska break; 867cdf63a70SMartin Matuska } 8686c95142eSMartin Matuska fe = fm->fm_extents; 8696c95142eSMartin Matuska for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) { 8706c95142eSMartin Matuska if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) { 8716c95142eSMartin Matuska /* The fe_length of the last block does not 8726c95142eSMartin Matuska * adjust itself to its size files. */ 8736c95142eSMartin Matuska int64_t length = fe->fe_length; 8746c95142eSMartin Matuska if (fe->fe_logical + length > (uint64_t)size) 8756c95142eSMartin Matuska length -= fe->fe_logical + length - size; 8766c95142eSMartin Matuska if (fe->fe_logical == 0 && length == size) { 8776c95142eSMartin Matuska /* This is not sparse. */ 8786c95142eSMartin Matuska do_fiemap = 0; 8796c95142eSMartin Matuska break; 8806c95142eSMartin Matuska } 8816c95142eSMartin Matuska if (length > 0) 8826c95142eSMartin Matuska archive_entry_sparse_add_entry(entry, 8836c95142eSMartin Matuska fe->fe_logical, length); 8846c95142eSMartin Matuska } 8856c95142eSMartin Matuska if (fe->fe_flags & FIEMAP_EXTENT_LAST) 8866c95142eSMartin Matuska do_fiemap = 0; 8876c95142eSMartin Matuska } 8886c95142eSMartin Matuska if (do_fiemap) { 8896c95142eSMartin Matuska fe = fm->fm_extents + fm->fm_mapped_extents -1; 8906c95142eSMartin Matuska fm->fm_start = fe->fe_logical + fe->fe_length; 8916c95142eSMartin Matuska } else 8926c95142eSMartin Matuska break; 8936c95142eSMartin Matuska } 894d5d08d29SMartin Matuska exit_setup_sparse_fiemap: 8956c95142eSMartin Matuska return (exit_sts); 8966c95142eSMartin Matuska } 8976c95142eSMartin Matuska 898d5d08d29SMartin Matuska #if !defined(SEEK_HOLE) || !defined(SEEK_DATA) 899d5d08d29SMartin Matuska static int 900d5d08d29SMartin Matuska setup_sparse(struct archive_read_disk *a, 901d5d08d29SMartin Matuska struct archive_entry *entry, int *fd) 902d5d08d29SMartin Matuska { 903d5d08d29SMartin Matuska return setup_sparse_fiemap(a, entry, fd); 904d5d08d29SMartin Matuska } 905d5d08d29SMartin Matuska #endif 906d5d08d29SMartin Matuska #endif /* defined(HAVE_LINUX_FIEMAP_H) */ 907d5d08d29SMartin Matuska 908d5d08d29SMartin Matuska #if defined(SEEK_HOLE) && defined(SEEK_DATA) 9096c95142eSMartin Matuska 9106c95142eSMartin Matuska /* 911d5d08d29SMartin Matuska * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris) 9126c95142eSMartin Matuska */ 9136c95142eSMartin Matuska 9146c95142eSMartin Matuska static int 9156c95142eSMartin Matuska setup_sparse(struct archive_read_disk *a, 916fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 9176c95142eSMartin Matuska { 9186c95142eSMartin Matuska int64_t size; 919d5d08d29SMartin Matuska off_t initial_off; 920d5d08d29SMartin Matuska off_t off_s, off_e; 9216c95142eSMartin Matuska int exit_sts = ARCHIVE_OK; 922cdf63a70SMartin Matuska int check_fully_sparse = 0; 9234657548dSMartin Matuska const char *path; 9246c95142eSMartin Matuska 9256c95142eSMartin Matuska if (archive_entry_filetype(entry) != AE_IFREG 9266c95142eSMartin Matuska || archive_entry_size(entry) <= 0 9276c95142eSMartin Matuska || archive_entry_hardlink(entry) != NULL) 9286c95142eSMartin Matuska return (ARCHIVE_OK); 9296c95142eSMartin Matuska 9306c95142eSMartin Matuska /* Does filesystem support the reporting of hole ? */ 931fd082e96SMartin Matuska if (*fd < 0) { 9324657548dSMartin Matuska path = archive_read_disk_entry_setup_path(a, entry, fd); 9334657548dSMartin Matuska if (path == NULL) 934fd082e96SMartin Matuska return (ARCHIVE_FAILED); 935fd082e96SMartin Matuska } 936fd082e96SMartin Matuska 937fd082e96SMartin Matuska if (*fd >= 0) { 938d5d08d29SMartin Matuska #ifdef _PC_MIN_HOLE_SIZE 939fd082e96SMartin Matuska if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0) 9406c95142eSMartin Matuska return (ARCHIVE_OK); 941d5d08d29SMartin Matuska #endif 942fd082e96SMartin Matuska initial_off = lseek(*fd, 0, SEEK_CUR); 9436c95142eSMartin Matuska if (initial_off != 0) 944fd082e96SMartin Matuska lseek(*fd, 0, SEEK_SET); 9456c95142eSMartin Matuska } else { 946d5d08d29SMartin Matuska #ifdef _PC_MIN_HOLE_SIZE 9476c95142eSMartin Matuska if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0) 9486c95142eSMartin Matuska return (ARCHIVE_OK); 949d5d08d29SMartin Matuska #endif 950acc60b03SMartin Matuska *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); 951fd082e96SMartin Matuska if (*fd < 0) { 9526c95142eSMartin Matuska archive_set_error(&a->archive, errno, 9536c95142eSMartin Matuska "Can't open `%s'", path); 9546c95142eSMartin Matuska return (ARCHIVE_FAILED); 9556c95142eSMartin Matuska } 956acc60b03SMartin Matuska __archive_ensure_cloexec_flag(*fd); 9576c95142eSMartin Matuska initial_off = 0; 9586c95142eSMartin Matuska } 9596c95142eSMartin Matuska 960d5d08d29SMartin Matuska #ifndef _PC_MIN_HOLE_SIZE 961d5d08d29SMartin Matuska /* Check if the underlying filesystem supports seek hole */ 962d5d08d29SMartin Matuska off_s = lseek(*fd, 0, SEEK_HOLE); 963d5d08d29SMartin Matuska if (off_s < 0) 964d5d08d29SMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H) 965d5d08d29SMartin Matuska return setup_sparse_fiemap(a, entry, fd); 966d5d08d29SMartin Matuska #else 967d5d08d29SMartin Matuska goto exit_setup_sparse; 968d5d08d29SMartin Matuska #endif 969d5d08d29SMartin Matuska else if (off_s > 0) 970d5d08d29SMartin Matuska lseek(*fd, 0, SEEK_SET); 971d5d08d29SMartin Matuska #endif 972d5d08d29SMartin Matuska 9736c95142eSMartin Matuska off_s = 0; 9746c95142eSMartin Matuska size = archive_entry_size(entry); 9756c95142eSMartin Matuska while (off_s < size) { 976fd082e96SMartin Matuska off_s = lseek(*fd, off_s, SEEK_DATA); 9776c95142eSMartin Matuska if (off_s == (off_t)-1) { 978cdf63a70SMartin Matuska if (errno == ENXIO) { 979cdf63a70SMartin Matuska /* no more hole */ 980cdf63a70SMartin Matuska if (archive_entry_sparse_count(entry) == 0) { 981cdf63a70SMartin Matuska /* Potentially a fully-sparse file. */ 982cdf63a70SMartin Matuska check_fully_sparse = 1; 983cdf63a70SMartin Matuska } 984cdf63a70SMartin Matuska break; 985cdf63a70SMartin Matuska } 9866c95142eSMartin Matuska archive_set_error(&a->archive, errno, 9876c95142eSMartin Matuska "lseek(SEEK_HOLE) failed"); 9886c95142eSMartin Matuska exit_sts = ARCHIVE_FAILED; 9896c95142eSMartin Matuska goto exit_setup_sparse; 9906c95142eSMartin Matuska } 991fd082e96SMartin Matuska off_e = lseek(*fd, off_s, SEEK_HOLE); 992fd082e96SMartin Matuska if (off_e == (off_t)-1) { 9936c95142eSMartin Matuska if (errno == ENXIO) { 994fd082e96SMartin Matuska off_e = lseek(*fd, 0, SEEK_END); 9956c95142eSMartin Matuska if (off_e != (off_t)-1) 9966c95142eSMartin Matuska break;/* no more data */ 9976c95142eSMartin Matuska } 9986c95142eSMartin Matuska archive_set_error(&a->archive, errno, 9996c95142eSMartin Matuska "lseek(SEEK_DATA) failed"); 10006c95142eSMartin Matuska exit_sts = ARCHIVE_FAILED; 10016c95142eSMartin Matuska goto exit_setup_sparse; 10026c95142eSMartin Matuska } 10036c95142eSMartin Matuska if (off_s == 0 && off_e == size) 1004a2e802b7SMartin Matuska break;/* This is not sparse. */ 10056c95142eSMartin Matuska archive_entry_sparse_add_entry(entry, off_s, 10066c95142eSMartin Matuska off_e - off_s); 10076c95142eSMartin Matuska off_s = off_e; 10086c95142eSMartin Matuska } 1009cdf63a70SMartin Matuska 1010cdf63a70SMartin Matuska if (check_fully_sparse) { 1011cdf63a70SMartin Matuska if (lseek(*fd, 0, SEEK_HOLE) == 0 && 1012cdf63a70SMartin Matuska lseek(*fd, 0, SEEK_END) == size) { 1013cdf63a70SMartin Matuska /* Fully sparse file; insert a zero-length "data" entry */ 1014cdf63a70SMartin Matuska archive_entry_sparse_add_entry(entry, 0, 0); 1015cdf63a70SMartin Matuska } 1016cdf63a70SMartin Matuska } 10176c95142eSMartin Matuska exit_setup_sparse: 1018fd082e96SMartin Matuska lseek(*fd, initial_off, SEEK_SET); 10196c95142eSMartin Matuska return (exit_sts); 10206c95142eSMartin Matuska } 10216c95142eSMartin Matuska 1022d5d08d29SMartin Matuska #elif !defined(HAVE_LINUX_FIEMAP_H) 10236c95142eSMartin Matuska 10246c95142eSMartin Matuska /* 10256c95142eSMartin Matuska * Generic (stub) sparse support. 10266c95142eSMartin Matuska */ 10276c95142eSMartin Matuska static int 10286c95142eSMartin Matuska setup_sparse(struct archive_read_disk *a, 1029fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 10306c95142eSMartin Matuska { 10316c95142eSMartin Matuska (void)a; /* UNUSED */ 10326c95142eSMartin Matuska (void)entry; /* UNUSED */ 10336c95142eSMartin Matuska (void)fd; /* UNUSED */ 10346c95142eSMartin Matuska return (ARCHIVE_OK); 10356c95142eSMartin Matuska } 10366c95142eSMartin Matuska 10376c95142eSMartin Matuska #endif 10386c95142eSMartin Matuska 10396c95142eSMartin Matuska #endif /* !defined(_WIN32) || defined(__CYGWIN__) */ 10406c95142eSMartin Matuska 1041