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" 296c22d9efSMartin 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 /* Mac OSX requires sys/types.h before sys/acl.h. */ 36caf54c4fSMartin Matuska #include <sys/types.h> 37caf54c4fSMartin Matuska #endif 38caf54c4fSMartin Matuska #ifdef HAVE_SYS_ACL_H 39caf54c4fSMartin Matuska #include <sys/acl.h> 40caf54c4fSMartin Matuska #endif 41caf54c4fSMartin Matuska #ifdef HAVE_SYS_EXTATTR_H 42caf54c4fSMartin Matuska #include <sys/extattr.h> 43caf54c4fSMartin Matuska #endif 446c95142eSMartin Matuska #ifdef HAVE_SYS_IOCTL_H 456c95142eSMartin Matuska #include <sys/ioctl.h> 466c95142eSMartin Matuska #endif 47caf54c4fSMartin Matuska #ifdef HAVE_SYS_PARAM_H 48caf54c4fSMartin Matuska #include <sys/param.h> 49caf54c4fSMartin Matuska #endif 50caf54c4fSMartin Matuska #ifdef HAVE_SYS_STAT_H 51caf54c4fSMartin Matuska #include <sys/stat.h> 52caf54c4fSMartin Matuska #endif 53acc60b03SMartin Matuska #if defined(HAVE_SYS_XATTR_H) 54caf54c4fSMartin Matuska #include <sys/xattr.h> 55acc60b03SMartin Matuska #elif defined(HAVE_ATTR_XATTR_H) 56acc60b03SMartin Matuska #include <attr/xattr.h> 57caf54c4fSMartin Matuska #endif 586c95142eSMartin Matuska #ifdef HAVE_SYS_EA_H 596c95142eSMartin Matuska #include <sys/ea.h> 606c95142eSMartin Matuska #endif 61caf54c4fSMartin Matuska #ifdef HAVE_ACL_LIBACL_H 62caf54c4fSMartin Matuska #include <acl/libacl.h> 63caf54c4fSMartin Matuska #endif 646c95142eSMartin Matuska #ifdef HAVE_COPYFILE_H 656c95142eSMartin Matuska #include <copyfile.h> 666c95142eSMartin Matuska #endif 67caf54c4fSMartin Matuska #ifdef HAVE_ERRNO_H 68caf54c4fSMartin Matuska #include <errno.h> 69caf54c4fSMartin Matuska #endif 706c95142eSMartin Matuska #ifdef HAVE_FCNTL_H 716c95142eSMartin Matuska #include <fcntl.h> 726c95142eSMartin Matuska #endif 73caf54c4fSMartin Matuska #ifdef HAVE_LIMITS_H 74caf54c4fSMartin Matuska #include <limits.h> 75caf54c4fSMartin Matuska #endif 76fd082e96SMartin Matuska #ifdef HAVE_LINUX_TYPES_H 77fd082e96SMartin Matuska #include <linux/types.h> 78fd082e96SMartin Matuska #endif 796c95142eSMartin Matuska #ifdef HAVE_LINUX_FIEMAP_H 806c95142eSMartin Matuska #include <linux/fiemap.h> 816c95142eSMartin Matuska #endif 826c95142eSMartin Matuska #ifdef HAVE_LINUX_FS_H 836c95142eSMartin Matuska #include <linux/fs.h> 846c95142eSMartin Matuska #endif 856c95142eSMartin Matuska /* 866c95142eSMartin Matuska * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. 876c95142eSMartin Matuska * As the include guards don't agree, the order of include is important. 886c95142eSMartin Matuska */ 896c95142eSMartin Matuska #ifdef HAVE_LINUX_EXT2_FS_H 906c95142eSMartin Matuska #include <linux/ext2_fs.h> /* for Linux file flags */ 916c95142eSMartin Matuska #endif 926c95142eSMartin Matuska #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) 936c95142eSMartin Matuska #include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */ 946c95142eSMartin Matuska #endif 956c95142eSMartin Matuska #ifdef HAVE_PATHS_H 966c95142eSMartin Matuska #include <paths.h> 976c95142eSMartin Matuska #endif 986c95142eSMartin Matuska #ifdef HAVE_UNISTD_H 996c95142eSMartin Matuska #include <unistd.h> 100caf54c4fSMartin Matuska #endif 101caf54c4fSMartin Matuska 102caf54c4fSMartin Matuska #include "archive.h" 103caf54c4fSMartin Matuska #include "archive_entry.h" 104caf54c4fSMartin Matuska #include "archive_private.h" 105caf54c4fSMartin Matuska #include "archive_read_disk_private.h" 106caf54c4fSMartin Matuska 107acc60b03SMartin Matuska #ifndef O_CLOEXEC 108acc60b03SMartin Matuska #define O_CLOEXEC 0 109acc60b03SMartin Matuska #endif 110acc60b03SMartin Matuska 111caf54c4fSMartin Matuska /* 112caf54c4fSMartin Matuska * Linux and FreeBSD plug this obvious hole in POSIX.1e in 113caf54c4fSMartin Matuska * different ways. 114caf54c4fSMartin Matuska */ 115caf54c4fSMartin Matuska #if HAVE_ACL_GET_PERM 116caf54c4fSMartin Matuska #define ACL_GET_PERM acl_get_perm 117caf54c4fSMartin Matuska #elif HAVE_ACL_GET_PERM_NP 118caf54c4fSMartin Matuska #define ACL_GET_PERM acl_get_perm_np 119caf54c4fSMartin Matuska #endif 120caf54c4fSMartin Matuska 12110ed66fdSMartin Matuska static int setup_acls(struct archive_read_disk *, 122fd082e96SMartin Matuska struct archive_entry *, int *fd); 1236c95142eSMartin Matuska static int setup_mac_metadata(struct archive_read_disk *, 124fd082e96SMartin Matuska struct archive_entry *, int *fd); 125caf54c4fSMartin Matuska static int setup_xattrs(struct archive_read_disk *, 126fd082e96SMartin Matuska struct archive_entry *, int *fd); 1276c95142eSMartin Matuska static int setup_sparse(struct archive_read_disk *, 128fd082e96SMartin Matuska struct archive_entry *, int *fd); 129d5d08d29SMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H) 130d5d08d29SMartin Matuska static int setup_sparse_fiemap(struct archive_read_disk *, 131d5d08d29SMartin Matuska struct archive_entry *, int *fd); 132d5d08d29SMartin Matuska #endif 133caf54c4fSMartin Matuska 134caf54c4fSMartin Matuska int 135caf54c4fSMartin Matuska archive_read_disk_entry_from_file(struct archive *_a, 136caf54c4fSMartin Matuska struct archive_entry *entry, 1376c95142eSMartin Matuska int fd, 1386c95142eSMartin Matuska const struct stat *st) 139caf54c4fSMartin Matuska { 140caf54c4fSMartin Matuska struct archive_read_disk *a = (struct archive_read_disk *)_a; 141caf54c4fSMartin Matuska const char *path, *name; 142caf54c4fSMartin Matuska struct stat s; 143caf54c4fSMartin Matuska int initial_fd = fd; 144caf54c4fSMartin Matuska int r, r1; 145caf54c4fSMartin Matuska 146caf54c4fSMartin Matuska archive_clear_error(_a); 147caf54c4fSMartin Matuska path = archive_entry_sourcepath(entry); 148caf54c4fSMartin Matuska if (path == NULL) 149caf54c4fSMartin Matuska path = archive_entry_pathname(entry); 150caf54c4fSMartin Matuska 1516c95142eSMartin Matuska if (a->tree == NULL) { 152caf54c4fSMartin Matuska if (st == NULL) { 153caf54c4fSMartin Matuska #if HAVE_FSTAT 154caf54c4fSMartin Matuska if (fd >= 0) { 155caf54c4fSMartin Matuska if (fstat(fd, &s) != 0) { 156caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 157caf54c4fSMartin Matuska "Can't fstat"); 158caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 159caf54c4fSMartin Matuska } 160caf54c4fSMartin Matuska } else 161caf54c4fSMartin Matuska #endif 162caf54c4fSMartin Matuska #if HAVE_LSTAT 163caf54c4fSMartin Matuska if (!a->follow_symlinks) { 164caf54c4fSMartin Matuska if (lstat(path, &s) != 0) { 165caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 166caf54c4fSMartin Matuska "Can't lstat %s", path); 167caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 168caf54c4fSMartin Matuska } 169caf54c4fSMartin Matuska } else 170caf54c4fSMartin Matuska #endif 171caf54c4fSMartin Matuska if (stat(path, &s) != 0) { 172caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 1736c95142eSMartin Matuska "Can't stat %s", path); 174caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 175caf54c4fSMartin Matuska } 176caf54c4fSMartin Matuska st = &s; 177caf54c4fSMartin Matuska } 178caf54c4fSMartin Matuska archive_entry_copy_stat(entry, st); 17910ed66fdSMartin Matuska } 18010ed66fdSMartin Matuska 181caf54c4fSMartin Matuska /* Lookup uname/gname */ 182caf54c4fSMartin Matuska name = archive_read_disk_uname(_a, archive_entry_uid(entry)); 183caf54c4fSMartin Matuska if (name != NULL) 184caf54c4fSMartin Matuska archive_entry_copy_uname(entry, name); 185caf54c4fSMartin Matuska name = archive_read_disk_gname(_a, archive_entry_gid(entry)); 186caf54c4fSMartin Matuska if (name != NULL) 187caf54c4fSMartin Matuska archive_entry_copy_gname(entry, name); 188caf54c4fSMartin Matuska 189caf54c4fSMartin Matuska #ifdef HAVE_STRUCT_STAT_ST_FLAGS 190caf54c4fSMartin Matuska /* On FreeBSD, we get flags for free with the stat. */ 191caf54c4fSMartin Matuska /* TODO: Does this belong in copy_stat()? */ 192caf54c4fSMartin Matuska if (st->st_flags != 0) 193caf54c4fSMartin Matuska archive_entry_set_fflags(entry, st->st_flags, 0); 194caf54c4fSMartin Matuska #endif 195caf54c4fSMartin Matuska 1966c95142eSMartin Matuska #if defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) 1976c95142eSMartin Matuska /* Linux requires an extra ioctl to pull the flags. Although 1986c95142eSMartin Matuska * this is an extra step, it has a nice side-effect: We get an 1996c95142eSMartin Matuska * open file descriptor which we can use in the subsequent lookups. */ 2006c95142eSMartin Matuska if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { 201fd082e96SMartin Matuska if (fd < 0) { 202fd082e96SMartin Matuska if (a->tree != NULL) 203fd082e96SMartin Matuska fd = a->open_on_current_dir(a->tree, path, 204acc60b03SMartin Matuska O_RDONLY | O_NONBLOCK | O_CLOEXEC); 205fd082e96SMartin Matuska else 206acc60b03SMartin Matuska fd = open(path, O_RDONLY | O_NONBLOCK | 207acc60b03SMartin Matuska O_CLOEXEC); 208acc60b03SMartin Matuska __archive_ensure_cloexec_flag(fd); 209fd082e96SMartin Matuska } 2106c95142eSMartin Matuska if (fd >= 0) { 211acc60b03SMartin Matuska int stflags; 2126c95142eSMartin Matuska r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); 2136c95142eSMartin Matuska if (r == 0 && stflags != 0) 2146c95142eSMartin Matuska archive_entry_set_fflags(entry, stflags, 0); 2156c95142eSMartin Matuska } 2166c95142eSMartin Matuska } 2176c95142eSMartin Matuska #endif 2186c95142eSMartin Matuska 2196c95142eSMartin Matuska #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) 220caf54c4fSMartin Matuska if (S_ISLNK(st->st_mode)) { 2216c95142eSMartin Matuska size_t linkbuffer_len = st->st_size + 1; 2226c95142eSMartin Matuska char *linkbuffer; 2236c95142eSMartin Matuska int lnklen; 2246c95142eSMartin Matuska 2256c95142eSMartin Matuska linkbuffer = malloc(linkbuffer_len); 2266c95142eSMartin Matuska if (linkbuffer == NULL) { 2276c95142eSMartin Matuska archive_set_error(&a->archive, ENOMEM, 2286c95142eSMartin Matuska "Couldn't read link data"); 2296c95142eSMartin Matuska return (ARCHIVE_FAILED); 2306c95142eSMartin Matuska } 231fd082e96SMartin Matuska if (a->tree != NULL) { 2326c95142eSMartin Matuska #ifdef HAVE_READLINKAT 233fd082e96SMartin Matuska lnklen = readlinkat(a->tree_current_dir_fd(a->tree), 234fd082e96SMartin Matuska path, linkbuffer, linkbuffer_len); 235fd082e96SMartin Matuska #else 236fd082e96SMartin Matuska if (a->tree_enter_working_dir(a->tree) != 0) { 237fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 238fd082e96SMartin Matuska "Couldn't read link data"); 239fd082e96SMartin Matuska free(linkbuffer); 240fd082e96SMartin Matuska return (ARCHIVE_FAILED); 241fd082e96SMartin Matuska } 242fd082e96SMartin Matuska lnklen = readlink(path, linkbuffer, linkbuffer_len); 2436c95142eSMartin Matuska #endif /* HAVE_READLINKAT */ 244fd082e96SMartin Matuska } else 2456c95142eSMartin Matuska lnklen = readlink(path, linkbuffer, linkbuffer_len); 246caf54c4fSMartin Matuska if (lnklen < 0) { 247caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 248caf54c4fSMartin Matuska "Couldn't read link data"); 2496c95142eSMartin Matuska free(linkbuffer); 250caf54c4fSMartin Matuska return (ARCHIVE_FAILED); 251caf54c4fSMartin Matuska } 252caf54c4fSMartin Matuska linkbuffer[lnklen] = 0; 253caf54c4fSMartin Matuska archive_entry_set_symlink(entry, linkbuffer); 2546c95142eSMartin Matuska free(linkbuffer); 255caf54c4fSMartin Matuska } 2566c95142eSMartin Matuska #endif /* HAVE_READLINK || HAVE_READLINKAT */ 257caf54c4fSMartin Matuska 25810ed66fdSMartin Matuska r = setup_acls(a, entry, &fd); 259cdf63a70SMartin Matuska if (!a->suppress_xattr) { 260fd082e96SMartin Matuska r1 = setup_xattrs(a, entry, &fd); 261caf54c4fSMartin Matuska if (r1 < r) 262caf54c4fSMartin Matuska r = r1; 263cdf63a70SMartin Matuska } 264fd082e96SMartin Matuska if (a->enable_copyfile) { 265fd082e96SMartin Matuska r1 = setup_mac_metadata(a, entry, &fd); 2666c95142eSMartin Matuska if (r1 < r) 2676c95142eSMartin Matuska r = r1; 268fd082e96SMartin Matuska } 269fd082e96SMartin Matuska r1 = setup_sparse(a, entry, &fd); 2706c95142eSMartin Matuska if (r1 < r) 2716c95142eSMartin Matuska r = r1; 2726c95142eSMartin Matuska 273caf54c4fSMartin Matuska /* If we opened the file earlier in this function, close it. */ 274caf54c4fSMartin Matuska if (initial_fd != fd) 275caf54c4fSMartin Matuska close(fd); 276caf54c4fSMartin Matuska return (r); 277caf54c4fSMartin Matuska } 278caf54c4fSMartin Matuska 2796c95142eSMartin Matuska #if defined(__APPLE__) && defined(HAVE_COPYFILE_H) 2806c95142eSMartin Matuska /* 2816c95142eSMartin Matuska * The Mac OS "copyfile()" API copies the extended metadata for a 2826c95142eSMartin Matuska * file into a separate file in AppleDouble format (see RFC 1740). 2836c95142eSMartin Matuska * 2846c95142eSMartin Matuska * Mac OS tar and cpio implementations store this extended 2856c95142eSMartin Matuska * metadata as a separate entry just before the regular entry 2866c95142eSMartin Matuska * with a "._" prefix added to the filename. 2876c95142eSMartin Matuska * 2886c95142eSMartin Matuska * Note that this is currently done unconditionally; the tar program has 2896c95142eSMartin Matuska * an option to discard this information before the archive is written. 2906c95142eSMartin Matuska * 2916c95142eSMartin Matuska * TODO: If there's a failure, report it and return ARCHIVE_WARN. 2926c95142eSMartin Matuska */ 2936c95142eSMartin Matuska static int 2946c95142eSMartin Matuska setup_mac_metadata(struct archive_read_disk *a, 295fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 2966c95142eSMartin Matuska { 2976c95142eSMartin Matuska int tempfd = -1; 2986c95142eSMartin Matuska int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR; 2996c95142eSMartin Matuska struct stat copyfile_stat; 3006c95142eSMartin Matuska int ret = ARCHIVE_OK; 301acc60b03SMartin Matuska void *buff = NULL; 3026c95142eSMartin Matuska int have_attrs; 303acc60b03SMartin Matuska const char *name, *tempdir; 304acc60b03SMartin Matuska struct archive_string tempfile; 3056c95142eSMartin Matuska 306fd082e96SMartin Matuska (void)fd; /* UNUSED */ 3076c95142eSMartin Matuska name = archive_entry_sourcepath(entry); 3086c95142eSMartin Matuska if (name == NULL) 3096c95142eSMartin Matuska name = archive_entry_pathname(entry); 3106c95142eSMartin Matuska if (name == NULL) { 3116c95142eSMartin Matuska archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 3126c95142eSMartin Matuska "Can't open file to read extended attributes: No name"); 3136c95142eSMartin Matuska return (ARCHIVE_WARN); 3146c95142eSMartin Matuska } 3156c95142eSMartin Matuska 316fd082e96SMartin Matuska if (a->tree != NULL) { 317fd082e96SMartin Matuska if (a->tree_enter_working_dir(a->tree) != 0) { 318fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 319fd082e96SMartin Matuska "Couldn't change dir"); 320fd082e96SMartin Matuska return (ARCHIVE_FAILED); 321fd082e96SMartin Matuska } 322fd082e96SMartin Matuska } 323fd082e96SMartin Matuska 3246c95142eSMartin Matuska /* Short-circuit if there's nothing to do. */ 3256c95142eSMartin Matuska have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK); 3266c95142eSMartin Matuska if (have_attrs == -1) { 3276c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3286c95142eSMartin Matuska "Could not check extended attributes"); 3296c95142eSMartin Matuska return (ARCHIVE_WARN); 3306c95142eSMartin Matuska } 3316c95142eSMartin Matuska if (have_attrs == 0) 3326c95142eSMartin Matuska return (ARCHIVE_OK); 3336c95142eSMartin Matuska 3346c95142eSMartin Matuska tempdir = NULL; 3356c95142eSMartin Matuska if (issetugid() == 0) 3366c95142eSMartin Matuska tempdir = getenv("TMPDIR"); 3376c95142eSMartin Matuska if (tempdir == NULL) 3386c95142eSMartin Matuska tempdir = _PATH_TMP; 339acc60b03SMartin Matuska archive_string_init(&tempfile); 340acc60b03SMartin Matuska archive_strcpy(&tempfile, tempdir); 341acc60b03SMartin Matuska archive_strcat(&tempfile, "tar.md.XXXXXX"); 342acc60b03SMartin Matuska tempfd = mkstemp(tempfile.s); 343acc60b03SMartin Matuska if (tempfd < 0) { 344acc60b03SMartin Matuska archive_set_error(&a->archive, errno, 345acc60b03SMartin Matuska "Could not open extended attribute file"); 346acc60b03SMartin Matuska ret = ARCHIVE_WARN; 347acc60b03SMartin Matuska goto cleanup; 348acc60b03SMartin Matuska } 349acc60b03SMartin Matuska __archive_ensure_cloexec_flag(tempfd); 3506c95142eSMartin Matuska 3516c95142eSMartin Matuska /* XXX I wish copyfile() could pack directly to a memory 3526c95142eSMartin Matuska * buffer; that would avoid the temp file here. For that 3536c95142eSMartin Matuska * matter, it would be nice if fcopyfile() actually worked, 3546c95142eSMartin Matuska * that would reduce the many open/close races here. */ 355acc60b03SMartin Matuska if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) { 3566c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3576c95142eSMartin Matuska "Could not pack extended attributes"); 3586c95142eSMartin Matuska ret = ARCHIVE_WARN; 3596c95142eSMartin Matuska goto cleanup; 3606c95142eSMartin Matuska } 3616c95142eSMartin Matuska if (fstat(tempfd, ©file_stat)) { 3626c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3636c95142eSMartin Matuska "Could not check size of extended attributes"); 3646c95142eSMartin Matuska ret = ARCHIVE_WARN; 3656c95142eSMartin Matuska goto cleanup; 3666c95142eSMartin Matuska } 3676c95142eSMartin Matuska buff = malloc(copyfile_stat.st_size); 3686c95142eSMartin Matuska if (buff == NULL) { 3696c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3706c95142eSMartin Matuska "Could not allocate memory for extended attributes"); 3716c95142eSMartin Matuska ret = ARCHIVE_WARN; 3726c95142eSMartin Matuska goto cleanup; 3736c95142eSMartin Matuska } 3746c95142eSMartin Matuska if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) { 3756c95142eSMartin Matuska archive_set_error(&a->archive, errno, 3766c95142eSMartin Matuska "Could not read extended attributes into memory"); 3776c95142eSMartin Matuska ret = ARCHIVE_WARN; 3786c95142eSMartin Matuska goto cleanup; 3796c95142eSMartin Matuska } 3806c95142eSMartin Matuska archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size); 3816c95142eSMartin Matuska 3826c95142eSMartin Matuska cleanup: 383acc60b03SMartin Matuska if (tempfd >= 0) { 3846c95142eSMartin Matuska close(tempfd); 385acc60b03SMartin Matuska unlink(tempfile.s); 386acc60b03SMartin Matuska } 387acc60b03SMartin Matuska archive_string_free(&tempfile); 388acc60b03SMartin Matuska free(buff); 3896c95142eSMartin Matuska return (ret); 3906c95142eSMartin Matuska } 3916c95142eSMartin Matuska 3926c95142eSMartin Matuska #else 3936c95142eSMartin Matuska 3946c95142eSMartin Matuska /* 3956c95142eSMartin Matuska * Stub implementation for non-Mac systems. 3966c95142eSMartin Matuska */ 3976c95142eSMartin Matuska static int 3986c95142eSMartin Matuska setup_mac_metadata(struct archive_read_disk *a, 399fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 4006c95142eSMartin Matuska { 4016c95142eSMartin Matuska (void)a; /* UNUSED */ 4026c95142eSMartin Matuska (void)entry; /* UNUSED */ 4036c95142eSMartin Matuska (void)fd; /* UNUSED */ 4046c95142eSMartin Matuska return (ARCHIVE_OK); 4056c95142eSMartin Matuska } 4066c95142eSMartin Matuska #endif 4076c95142eSMartin Matuska 4086c95142eSMartin Matuska 409cdf63a70SMartin Matuska #ifdef HAVE_POSIX_ACL 41010ed66fdSMartin Matuska static int translate_acl(struct archive_read_disk *a, 411caf54c4fSMartin Matuska struct archive_entry *entry, acl_t acl, int archive_entry_acl_type); 412caf54c4fSMartin Matuska 413caf54c4fSMartin Matuska static int 41410ed66fdSMartin Matuska setup_acls(struct archive_read_disk *a, 415fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 416caf54c4fSMartin Matuska { 417caf54c4fSMartin Matuska const char *accpath; 418caf54c4fSMartin Matuska acl_t acl; 41910ed66fdSMartin Matuska int r; 420caf54c4fSMartin Matuska 421caf54c4fSMartin Matuska accpath = archive_entry_sourcepath(entry); 422caf54c4fSMartin Matuska if (accpath == NULL) 423caf54c4fSMartin Matuska accpath = archive_entry_pathname(entry); 424caf54c4fSMartin Matuska 42547af42f8SMartin Matuska if (*fd < 0 && a->tree != NULL) { 42647af42f8SMartin Matuska if (a->follow_symlinks || 42747af42f8SMartin Matuska archive_entry_filetype(entry) != AE_IFLNK) 42847af42f8SMartin Matuska *fd = a->open_on_current_dir(a->tree, 42947af42f8SMartin Matuska accpath, O_RDONLY | O_NONBLOCK); 43047af42f8SMartin Matuska if (*fd < 0) { 43147af42f8SMartin Matuska if (a->tree_enter_working_dir(a->tree) != 0) { 43247af42f8SMartin Matuska archive_set_error(&a->archive, errno, 43347af42f8SMartin Matuska "Couldn't access %s", accpath); 43447af42f8SMartin Matuska return (ARCHIVE_FAILED); 43547af42f8SMartin Matuska } 43647af42f8SMartin Matuska } 43747af42f8SMartin Matuska } 43847af42f8SMartin Matuska 439caf54c4fSMartin Matuska archive_entry_acl_clear(entry); 440caf54c4fSMartin Matuska 44147af42f8SMartin Matuska acl = NULL; 44247af42f8SMartin Matuska 443cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 444e9ed7ea4SMartin Matuska /* Try NFSv4 ACL first. */ 44510ed66fdSMartin Matuska if (*fd >= 0) 44647af42f8SMartin Matuska #if HAVE_ACL_GET_FD_NP 44747af42f8SMartin Matuska acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4); 44847af42f8SMartin Matuska #else 449c438d384SMartin Matuska acl = acl_get_fd(*fd); 45047af42f8SMartin Matuska #endif 45110ed66fdSMartin Matuska #if HAVE_ACL_GET_LINK_NP 452c438d384SMartin Matuska else if (!a->follow_symlinks) 45310ed66fdSMartin Matuska acl = acl_get_link_np(accpath, ACL_TYPE_NFS4); 45410ed66fdSMartin Matuska #else 455c438d384SMartin Matuska else if ((!a->follow_symlinks) 456c438d384SMartin Matuska && (archive_entry_filetype(entry) == AE_IFLNK)) 457c438d384SMartin Matuska /* We can't get the ACL of a symlink, so we assume it can't 458c438d384SMartin Matuska have one. */ 45910ed66fdSMartin Matuska acl = NULL; 46010ed66fdSMartin Matuska #endif 461c438d384SMartin Matuska else 462c4676089SMartin Matuska acl = acl_get_file(accpath, ACL_TYPE_NFS4); 46347af42f8SMartin Matuska 464c4676089SMartin Matuska #if HAVE_ACL_IS_TRIVIAL_NP 46547af42f8SMartin Matuska if (acl != NULL && acl_is_trivial_np(acl, &r) == 0) { 466c4676089SMartin Matuska /* Ignore "trivial" ACLs that just mirror the file mode. */ 467c4676089SMartin Matuska if (r) { 46810ed66fdSMartin Matuska acl_free(acl); 469c4676089SMartin Matuska acl = NULL; 47047af42f8SMartin Matuska /* 47147af42f8SMartin Matuska * Simultaneous NFSv4 and POSIX.1e ACLs for the same 47247af42f8SMartin Matuska * entry are not allowed, so we should return here 47347af42f8SMartin Matuska */ 47447af42f8SMartin Matuska return (ARCHIVE_OK); 47547af42f8SMartin Matuska } 476c4676089SMartin Matuska } 477c4676089SMartin Matuska #endif 478c438d384SMartin Matuska if (acl != NULL) { 47924113d8cSMartin Matuska r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); 480c4676089SMartin Matuska acl_free(acl); 48124113d8cSMartin Matuska if (r != ARCHIVE_OK) { 48224113d8cSMartin Matuska archive_set_error(&a->archive, errno, 48324113d8cSMartin Matuska "Couldn't translate NFSv4 ACLs: %s", accpath); 48424113d8cSMartin Matuska } 48524113d8cSMartin Matuska return (r); 486c4676089SMartin Matuska } 48747af42f8SMartin Matuska #endif /* ACL_TYPE_NFS4 */ 488fd082e96SMartin Matuska 489caf54c4fSMartin Matuska /* Retrieve access ACL from file. */ 490fd082e96SMartin Matuska if (*fd >= 0) 491fd082e96SMartin Matuska acl = acl_get_fd(*fd); 492caf54c4fSMartin Matuska #if HAVE_ACL_GET_LINK_NP 493c438d384SMartin Matuska else if (!a->follow_symlinks) 494caf54c4fSMartin Matuska acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); 495caf54c4fSMartin Matuska #else 496c438d384SMartin Matuska else if ((!a->follow_symlinks) 497c438d384SMartin Matuska && (archive_entry_filetype(entry) == AE_IFLNK)) 498c438d384SMartin Matuska /* We can't get the ACL of a symlink, so we assume it can't 499c438d384SMartin Matuska have one. */ 500caf54c4fSMartin Matuska acl = NULL; 501caf54c4fSMartin Matuska #endif 502c438d384SMartin Matuska else 503caf54c4fSMartin Matuska acl = acl_get_file(accpath, ACL_TYPE_ACCESS); 50447af42f8SMartin Matuska 50547af42f8SMartin Matuska #if HAVE_ACL_IS_TRIVIAL_NP 50647af42f8SMartin Matuska /* Ignore "trivial" ACLs that just mirror the file mode. */ 50747af42f8SMartin Matuska if (acl != NULL && acl_is_trivial_np(acl, &r) == 0) { 50847af42f8SMartin Matuska if (r) { 50947af42f8SMartin Matuska acl_free(acl); 51047af42f8SMartin Matuska acl = NULL; 51147af42f8SMartin Matuska } 51247af42f8SMartin Matuska } 51347af42f8SMartin Matuska #endif 51447af42f8SMartin Matuska 515caf54c4fSMartin Matuska if (acl != NULL) { 51624113d8cSMartin Matuska r = translate_acl(a, entry, acl, 517caf54c4fSMartin Matuska ARCHIVE_ENTRY_ACL_TYPE_ACCESS); 518caf54c4fSMartin Matuska acl_free(acl); 51947af42f8SMartin Matuska acl = NULL; 52024113d8cSMartin Matuska if (r != ARCHIVE_OK) { 52124113d8cSMartin Matuska archive_set_error(&a->archive, errno, 52224113d8cSMartin Matuska "Couldn't translate access ACLs: %s", accpath); 52324113d8cSMartin Matuska return (r); 52424113d8cSMartin Matuska } 525caf54c4fSMartin Matuska } 526caf54c4fSMartin Matuska 527caf54c4fSMartin Matuska /* Only directories can have default ACLs. */ 528caf54c4fSMartin Matuska if (S_ISDIR(archive_entry_mode(entry))) { 529caf54c4fSMartin Matuska acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); 530caf54c4fSMartin Matuska if (acl != NULL) { 53124113d8cSMartin Matuska r = translate_acl(a, entry, acl, 532caf54c4fSMartin Matuska ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); 533caf54c4fSMartin Matuska acl_free(acl); 53424113d8cSMartin Matuska if (r != ARCHIVE_OK) { 53524113d8cSMartin Matuska archive_set_error(&a->archive, errno, 53624113d8cSMartin Matuska "Couldn't translate default ACLs: %s", 53724113d8cSMartin Matuska accpath); 53824113d8cSMartin Matuska return (r); 53924113d8cSMartin Matuska } 540caf54c4fSMartin Matuska } 541caf54c4fSMartin Matuska } 542caf54c4fSMartin Matuska return (ARCHIVE_OK); 543caf54c4fSMartin Matuska } 544caf54c4fSMartin Matuska 545caf54c4fSMartin Matuska /* 54610ed66fdSMartin Matuska * Translate system ACL into libarchive internal structure. 547caf54c4fSMartin Matuska */ 54810ed66fdSMartin Matuska 54910ed66fdSMartin Matuska static struct { 55010ed66fdSMartin Matuska int archive_perm; 55110ed66fdSMartin Matuska int platform_perm; 55210ed66fdSMartin Matuska } acl_perm_map[] = { 55310ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, 55410ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, 55510ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, 556cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 55710ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, 55810ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, 55910ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, 56010ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, 56110ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, 56210ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, 56310ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, 56410ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, 56510ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, 56610ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, 56710ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, 56810ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, 56910ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL}, 57010ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, 57110ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, 57210ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} 573cdf63a70SMartin Matuska #endif 57410ed66fdSMartin Matuska }; 57510ed66fdSMartin Matuska 576cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 57710ed66fdSMartin Matuska static struct { 57810ed66fdSMartin Matuska int archive_inherit; 57910ed66fdSMartin Matuska int platform_inherit; 58010ed66fdSMartin Matuska } acl_inherit_map[] = { 58110ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, 58210ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, 58310ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, 58410ed66fdSMartin Matuska {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY} 58510ed66fdSMartin Matuska }; 586cdf63a70SMartin Matuska #endif 58710ed66fdSMartin Matuska static int 58810ed66fdSMartin Matuska translate_acl(struct archive_read_disk *a, 58910ed66fdSMartin Matuska struct archive_entry *entry, acl_t acl, int default_entry_acl_type) 590caf54c4fSMartin Matuska { 591caf54c4fSMartin Matuska acl_tag_t acl_tag; 592cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 59310ed66fdSMartin Matuska acl_entry_type_t acl_type; 59410ed66fdSMartin Matuska acl_flagset_t acl_flagset; 59524113d8cSMartin Matuska int brand; 596cdf63a70SMartin Matuska #endif 597caf54c4fSMartin Matuska acl_entry_t acl_entry; 598caf54c4fSMartin Matuska acl_permset_t acl_permset; 599cdf63a70SMartin Matuska int i, entry_acl_type; 60024113d8cSMartin Matuska int r, s, ae_id, ae_tag, ae_perm; 601caf54c4fSMartin Matuska const char *ae_name; 602caf54c4fSMartin Matuska 603cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 60410ed66fdSMartin Matuska // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 60510ed66fdSMartin Matuska // Make sure the "brand" on this ACL is consistent 60610ed66fdSMartin Matuska // with the default_entry_acl_type bits provided. 60724113d8cSMartin Matuska if (acl_get_brand_np(acl, &brand) != 0) { 60824113d8cSMartin Matuska archive_set_error(&a->archive, errno, 60924113d8cSMartin Matuska "Failed to read ACL brand"); 61024113d8cSMartin Matuska return (ARCHIVE_WARN); 61124113d8cSMartin Matuska } 61210ed66fdSMartin Matuska switch (brand) { 61310ed66fdSMartin Matuska case ACL_BRAND_POSIX: 61410ed66fdSMartin Matuska switch (default_entry_acl_type) { 61510ed66fdSMartin Matuska case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: 61610ed66fdSMartin Matuska case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: 61710ed66fdSMartin Matuska break; 61810ed66fdSMartin Matuska default: 61924113d8cSMartin Matuska archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 62024113d8cSMartin Matuska "Invalid ACL entry type for POSIX.1e ACL"); 62124113d8cSMartin Matuska return (ARCHIVE_WARN); 62210ed66fdSMartin Matuska } 62310ed66fdSMartin Matuska break; 62410ed66fdSMartin Matuska case ACL_BRAND_NFS4: 62510ed66fdSMartin Matuska if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 62624113d8cSMartin Matuska archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 62724113d8cSMartin Matuska "Invalid ACL entry type for NFSv4 ACL"); 62824113d8cSMartin Matuska return (ARCHIVE_WARN); 62910ed66fdSMartin Matuska } 63010ed66fdSMartin Matuska break; 63110ed66fdSMartin Matuska default: 63224113d8cSMartin Matuska archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 63324113d8cSMartin Matuska "Unknown ACL brand"); 63424113d8cSMartin Matuska return (ARCHIVE_WARN); 63510ed66fdSMartin Matuska } 636cdf63a70SMartin Matuska #endif 63710ed66fdSMartin Matuska 63810ed66fdSMartin Matuska 639caf54c4fSMartin Matuska s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); 64024113d8cSMartin Matuska if (s == -1) { 64124113d8cSMartin Matuska archive_set_error(&a->archive, errno, 64224113d8cSMartin Matuska "Failed to get first ACL entry"); 64324113d8cSMartin Matuska return (ARCHIVE_WARN); 64424113d8cSMartin Matuska } 645caf54c4fSMartin Matuska while (s == 1) { 646caf54c4fSMartin Matuska ae_id = -1; 647caf54c4fSMartin Matuska ae_name = NULL; 64810ed66fdSMartin Matuska ae_perm = 0; 649caf54c4fSMartin Matuska 65024113d8cSMartin Matuska if (acl_get_tag_type(acl_entry, &acl_tag) != 0) { 65124113d8cSMartin Matuska archive_set_error(&a->archive, errno, 65224113d8cSMartin Matuska "Failed to get ACL tag type"); 65324113d8cSMartin Matuska return (ARCHIVE_WARN); 65424113d8cSMartin Matuska } 65510ed66fdSMartin Matuska switch (acl_tag) { 65610ed66fdSMartin Matuska case ACL_USER: 657caf54c4fSMartin Matuska ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); 658caf54c4fSMartin Matuska ae_name = archive_read_disk_uname(&a->archive, ae_id); 659caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_USER; 66010ed66fdSMartin Matuska break; 66110ed66fdSMartin Matuska case ACL_GROUP: 662caf54c4fSMartin Matuska ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); 663caf54c4fSMartin Matuska ae_name = archive_read_disk_gname(&a->archive, ae_id); 664caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_GROUP; 66510ed66fdSMartin Matuska break; 66610ed66fdSMartin Matuska case ACL_MASK: 667caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_MASK; 66810ed66fdSMartin Matuska break; 66910ed66fdSMartin Matuska case ACL_USER_OBJ: 670caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; 67110ed66fdSMartin Matuska break; 67210ed66fdSMartin Matuska case ACL_GROUP_OBJ: 673caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; 67410ed66fdSMartin Matuska break; 67510ed66fdSMartin Matuska case ACL_OTHER: 676caf54c4fSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_OTHER; 67710ed66fdSMartin Matuska break; 678cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 67910ed66fdSMartin Matuska case ACL_EVERYONE: 68010ed66fdSMartin Matuska ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; 68110ed66fdSMartin Matuska break; 682cdf63a70SMartin Matuska #endif 68310ed66fdSMartin Matuska default: 684caf54c4fSMartin Matuska /* Skip types that libarchive can't support. */ 68588b860fcSMartin Matuska s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); 686caf54c4fSMartin Matuska continue; 687caf54c4fSMartin Matuska } 688caf54c4fSMartin Matuska 68924113d8cSMartin Matuska // XXX acl_type maps to allow/deny/audit/YYYY bits 69010ed66fdSMartin Matuska entry_acl_type = default_entry_acl_type; 691cdf63a70SMartin Matuska #ifdef ACL_TYPE_NFS4 69224113d8cSMartin Matuska if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { 69324113d8cSMartin Matuska /* 694a2e802b7SMartin Matuska * acl_get_entry_type_np() fails with non-NFSv4 ACLs 69524113d8cSMartin Matuska */ 69624113d8cSMartin Matuska if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) { 69724113d8cSMartin Matuska archive_set_error(&a->archive, errno, "Failed " 69824113d8cSMartin Matuska "to get ACL type from a NFSv4 ACL entry"); 69924113d8cSMartin Matuska return (ARCHIVE_WARN); 70024113d8cSMartin Matuska } 70110ed66fdSMartin Matuska switch (acl_type) { 70210ed66fdSMartin Matuska case ACL_ENTRY_TYPE_ALLOW: 70310ed66fdSMartin Matuska entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; 70410ed66fdSMartin Matuska break; 70510ed66fdSMartin Matuska case ACL_ENTRY_TYPE_DENY: 70610ed66fdSMartin Matuska entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; 70710ed66fdSMartin Matuska break; 70810ed66fdSMartin Matuska case ACL_ENTRY_TYPE_AUDIT: 70910ed66fdSMartin Matuska entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; 71010ed66fdSMartin Matuska break; 71110ed66fdSMartin Matuska case ACL_ENTRY_TYPE_ALARM: 71210ed66fdSMartin Matuska entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; 71310ed66fdSMartin Matuska break; 71424113d8cSMartin Matuska default: 71524113d8cSMartin Matuska archive_set_error(&a->archive, errno, 71624113d8cSMartin Matuska "Invalid NFSv4 ACL entry type"); 71724113d8cSMartin Matuska return (ARCHIVE_WARN); 71810ed66fdSMartin Matuska } 71910ed66fdSMartin Matuska 72010ed66fdSMartin Matuska /* 72110ed66fdSMartin Matuska * Libarchive stores "flag" (NFSv4 inheritance bits) 72210ed66fdSMartin Matuska * in the ae_perm bitmap. 72324113d8cSMartin Matuska * 72424113d8cSMartin Matuska * acl_get_flagset_np() fails with non-NFSv4 ACLs 72510ed66fdSMartin Matuska */ 72624113d8cSMartin Matuska if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { 72724113d8cSMartin Matuska archive_set_error(&a->archive, errno, 72824113d8cSMartin Matuska "Failed to get flagset from a NFSv4 ACL entry"); 72924113d8cSMartin Matuska return (ARCHIVE_WARN); 73024113d8cSMartin Matuska } 73110ed66fdSMartin Matuska for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { 73224113d8cSMartin Matuska r = acl_get_flag_np(acl_flagset, 73324113d8cSMartin Matuska acl_inherit_map[i].platform_inherit); 73424113d8cSMartin Matuska if (r == -1) { 73524113d8cSMartin Matuska archive_set_error(&a->archive, errno, 73624113d8cSMartin Matuska "Failed to check flag in a NFSv4 " 73724113d8cSMartin Matuska "ACL flagset"); 73824113d8cSMartin Matuska return (ARCHIVE_WARN); 73924113d8cSMartin Matuska } else if (r) 74010ed66fdSMartin Matuska ae_perm |= acl_inherit_map[i].archive_inherit; 741cfa49a9bSMartin Matuska } 74210ed66fdSMartin Matuska } 743cdf63a70SMartin Matuska #endif 74410ed66fdSMartin Matuska 74524113d8cSMartin Matuska if (acl_get_permset(acl_entry, &acl_permset) != 0) { 74624113d8cSMartin Matuska archive_set_error(&a->archive, errno, 74724113d8cSMartin Matuska "Failed to get ACL permission set"); 74824113d8cSMartin Matuska return (ARCHIVE_WARN); 74924113d8cSMartin Matuska } 75010ed66fdSMartin Matuska for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { 751caf54c4fSMartin Matuska /* 752caf54c4fSMartin Matuska * acl_get_perm() is spelled differently on different 753caf54c4fSMartin Matuska * platforms; see above. 754caf54c4fSMartin Matuska */ 75524113d8cSMartin Matuska r = ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm); 75624113d8cSMartin Matuska if (r == -1) { 75724113d8cSMartin Matuska archive_set_error(&a->archive, errno, 75824113d8cSMartin Matuska "Failed to check permission in an ACL permission set"); 75924113d8cSMartin Matuska return (ARCHIVE_WARN); 76024113d8cSMartin Matuska } else if (r) 76110ed66fdSMartin Matuska ae_perm |= acl_perm_map[i].archive_perm; 76210ed66fdSMartin Matuska } 763caf54c4fSMartin Matuska 764c438d384SMartin Matuska archive_entry_acl_add_entry(entry, entry_acl_type, 76510ed66fdSMartin Matuska ae_perm, ae_tag, 766caf54c4fSMartin Matuska ae_id, ae_name); 767caf54c4fSMartin Matuska 768caf54c4fSMartin Matuska s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); 76924113d8cSMartin Matuska if (s == -1) { 77024113d8cSMartin Matuska archive_set_error(&a->archive, errno, 77124113d8cSMartin Matuska "Failed to get next ACL entry"); 77224113d8cSMartin Matuska return (ARCHIVE_WARN); 77324113d8cSMartin Matuska } 774caf54c4fSMartin Matuska } 77510ed66fdSMartin Matuska return (ARCHIVE_OK); 776caf54c4fSMartin Matuska } 777caf54c4fSMartin Matuska #else 778caf54c4fSMartin Matuska static int 77910ed66fdSMartin Matuska setup_acls(struct archive_read_disk *a, 780acc60b03SMartin Matuska struct archive_entry *entry, int *fd) 781caf54c4fSMartin Matuska { 782caf54c4fSMartin Matuska (void)a; /* UNUSED */ 783caf54c4fSMartin Matuska (void)entry; /* UNUSED */ 784caf54c4fSMartin Matuska (void)fd; /* UNUSED */ 785caf54c4fSMartin Matuska return (ARCHIVE_OK); 786caf54c4fSMartin Matuska } 787caf54c4fSMartin Matuska #endif 788caf54c4fSMartin Matuska 7896c95142eSMartin Matuska #if (HAVE_FGETXATTR && HAVE_FLISTXATTR && HAVE_LISTXATTR && \ 7906c95142eSMartin Matuska HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR) || \ 7916c95142eSMartin Matuska (HAVE_FGETEA && HAVE_FLISTEA && HAVE_LISTEA) 792caf54c4fSMartin Matuska 793caf54c4fSMartin Matuska /* 7946c95142eSMartin Matuska * Linux and AIX extended attribute support. 795caf54c4fSMartin Matuska * 796caf54c4fSMartin Matuska * TODO: By using a stack-allocated buffer for the first 797caf54c4fSMartin Matuska * call to getxattr(), we might be able to avoid the second 798caf54c4fSMartin Matuska * call entirely. We only need the second call if the 799caf54c4fSMartin Matuska * stack-allocated buffer is too small. But a modest buffer 800caf54c4fSMartin Matuska * of 1024 bytes or so will often be big enough. Same applies 801caf54c4fSMartin Matuska * to listxattr(). 802caf54c4fSMartin Matuska */ 803caf54c4fSMartin Matuska 804caf54c4fSMartin Matuska 805caf54c4fSMartin Matuska static int 806caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, 807caf54c4fSMartin Matuska struct archive_entry *entry, const char *name, int fd) 808caf54c4fSMartin Matuska { 809caf54c4fSMartin Matuska ssize_t size; 810caf54c4fSMartin Matuska void *value = NULL; 811caf54c4fSMartin Matuska const char *accpath; 812caf54c4fSMartin Matuska 813caf54c4fSMartin Matuska accpath = archive_entry_sourcepath(entry); 814caf54c4fSMartin Matuska if (accpath == NULL) 815caf54c4fSMartin Matuska accpath = archive_entry_pathname(entry); 816caf54c4fSMartin Matuska 8176c95142eSMartin Matuska #if HAVE_FGETXATTR 8186c95142eSMartin Matuska if (fd >= 0) 8196c95142eSMartin Matuska size = fgetxattr(fd, name, NULL, 0); 8206c95142eSMartin Matuska else if (!a->follow_symlinks) 821caf54c4fSMartin Matuska size = lgetxattr(accpath, name, NULL, 0); 822caf54c4fSMartin Matuska else 823caf54c4fSMartin Matuska size = getxattr(accpath, name, NULL, 0); 8246c95142eSMartin Matuska #elif HAVE_FGETEA 8256c95142eSMartin Matuska if (fd >= 0) 8266c95142eSMartin Matuska size = fgetea(fd, name, NULL, 0); 8276c95142eSMartin Matuska else if (!a->follow_symlinks) 8286c95142eSMartin Matuska size = lgetea(accpath, name, NULL, 0); 8296c95142eSMartin Matuska else 8306c95142eSMartin Matuska size = getea(accpath, name, NULL, 0); 8316c95142eSMartin Matuska #endif 832caf54c4fSMartin Matuska 833caf54c4fSMartin Matuska if (size == -1) { 834caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 835caf54c4fSMartin Matuska "Couldn't query extended attribute"); 836caf54c4fSMartin Matuska return (ARCHIVE_WARN); 837caf54c4fSMartin Matuska } 838caf54c4fSMartin Matuska 839caf54c4fSMartin Matuska if (size > 0 && (value = malloc(size)) == NULL) { 840caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 841caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 842caf54c4fSMartin Matuska } 843caf54c4fSMartin Matuska 8446c95142eSMartin Matuska #if HAVE_FGETXATTR 8456c95142eSMartin Matuska if (fd >= 0) 8466c95142eSMartin Matuska size = fgetxattr(fd, name, value, size); 8476c95142eSMartin Matuska else if (!a->follow_symlinks) 848caf54c4fSMartin Matuska size = lgetxattr(accpath, name, value, size); 849caf54c4fSMartin Matuska else 850caf54c4fSMartin Matuska size = getxattr(accpath, name, value, size); 8516c95142eSMartin Matuska #elif HAVE_FGETEA 8526c95142eSMartin Matuska if (fd >= 0) 8536c95142eSMartin Matuska size = fgetea(fd, name, value, size); 8546c95142eSMartin Matuska else if (!a->follow_symlinks) 8556c95142eSMartin Matuska size = lgetea(accpath, name, value, size); 8566c95142eSMartin Matuska else 8576c95142eSMartin Matuska size = getea(accpath, name, value, size); 8586c95142eSMartin Matuska #endif 859caf54c4fSMartin Matuska 860caf54c4fSMartin Matuska if (size == -1) { 861caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 862caf54c4fSMartin Matuska "Couldn't read extended attribute"); 863caf54c4fSMartin Matuska return (ARCHIVE_WARN); 864caf54c4fSMartin Matuska } 865caf54c4fSMartin Matuska 866caf54c4fSMartin Matuska archive_entry_xattr_add_entry(entry, name, value, size); 867caf54c4fSMartin Matuska 868caf54c4fSMartin Matuska free(value); 869caf54c4fSMartin Matuska return (ARCHIVE_OK); 870caf54c4fSMartin Matuska } 871caf54c4fSMartin Matuska 872caf54c4fSMartin Matuska static int 873caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a, 874fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 875caf54c4fSMartin Matuska { 876caf54c4fSMartin Matuska char *list, *p; 877caf54c4fSMartin Matuska const char *path; 878caf54c4fSMartin Matuska ssize_t list_size; 879caf54c4fSMartin Matuska 880caf54c4fSMartin Matuska path = archive_entry_sourcepath(entry); 881caf54c4fSMartin Matuska if (path == NULL) 882caf54c4fSMartin Matuska path = archive_entry_pathname(entry); 883caf54c4fSMartin Matuska 884fd082e96SMartin Matuska if (*fd < 0 && a->tree != NULL) { 885fd082e96SMartin Matuska if (a->follow_symlinks || 886fd082e96SMartin Matuska archive_entry_filetype(entry) != AE_IFLNK) 887fd082e96SMartin Matuska *fd = a->open_on_current_dir(a->tree, path, 888fd082e96SMartin Matuska O_RDONLY | O_NONBLOCK); 889fd082e96SMartin Matuska if (*fd < 0) { 890fd082e96SMartin Matuska if (a->tree_enter_working_dir(a->tree) != 0) { 891fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 892fd082e96SMartin Matuska "Couldn't access %s", path); 893fd082e96SMartin Matuska return (ARCHIVE_FAILED); 894fd082e96SMartin Matuska } 895fd082e96SMartin Matuska } 896fd082e96SMartin Matuska } 897fd082e96SMartin Matuska 8986c95142eSMartin Matuska #if HAVE_FLISTXATTR 899fd082e96SMartin Matuska if (*fd >= 0) 900fd082e96SMartin Matuska list_size = flistxattr(*fd, NULL, 0); 9016c95142eSMartin Matuska else if (!a->follow_symlinks) 902caf54c4fSMartin Matuska list_size = llistxattr(path, NULL, 0); 903caf54c4fSMartin Matuska else 904caf54c4fSMartin Matuska list_size = listxattr(path, NULL, 0); 9056c95142eSMartin Matuska #elif HAVE_FLISTEA 906fd082e96SMartin Matuska if (*fd >= 0) 907fd082e96SMartin Matuska list_size = flistea(*fd, NULL, 0); 9086c95142eSMartin Matuska else if (!a->follow_symlinks) 9096c95142eSMartin Matuska list_size = llistea(path, NULL, 0); 9106c95142eSMartin Matuska else 9116c95142eSMartin Matuska list_size = listea(path, NULL, 0); 9126c95142eSMartin Matuska #endif 913caf54c4fSMartin Matuska 914caf54c4fSMartin Matuska if (list_size == -1) { 9156c95142eSMartin Matuska if (errno == ENOTSUP || errno == ENOSYS) 916caf54c4fSMartin Matuska return (ARCHIVE_OK); 917caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 918caf54c4fSMartin Matuska "Couldn't list extended attributes"); 919caf54c4fSMartin Matuska return (ARCHIVE_WARN); 920caf54c4fSMartin Matuska } 921caf54c4fSMartin Matuska 922caf54c4fSMartin Matuska if (list_size == 0) 923caf54c4fSMartin Matuska return (ARCHIVE_OK); 924caf54c4fSMartin Matuska 925caf54c4fSMartin Matuska if ((list = malloc(list_size)) == NULL) { 926caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 927caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 928caf54c4fSMartin Matuska } 929caf54c4fSMartin Matuska 9306c95142eSMartin Matuska #if HAVE_FLISTXATTR 931fd082e96SMartin Matuska if (*fd >= 0) 932fd082e96SMartin Matuska list_size = flistxattr(*fd, list, list_size); 9336c95142eSMartin Matuska else if (!a->follow_symlinks) 934caf54c4fSMartin Matuska list_size = llistxattr(path, list, list_size); 935caf54c4fSMartin Matuska else 936caf54c4fSMartin Matuska list_size = listxattr(path, list, list_size); 9376c95142eSMartin Matuska #elif HAVE_FLISTEA 938fd082e96SMartin Matuska if (*fd >= 0) 939fd082e96SMartin Matuska list_size = flistea(*fd, list, list_size); 9406c95142eSMartin Matuska else if (!a->follow_symlinks) 9416c95142eSMartin Matuska list_size = llistea(path, list, list_size); 9426c95142eSMartin Matuska else 9436c95142eSMartin Matuska list_size = listea(path, list, list_size); 9446c95142eSMartin Matuska #endif 945caf54c4fSMartin Matuska 946caf54c4fSMartin Matuska if (list_size == -1) { 947caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 948caf54c4fSMartin Matuska "Couldn't retrieve extended attributes"); 949caf54c4fSMartin Matuska free(list); 950caf54c4fSMartin Matuska return (ARCHIVE_WARN); 951caf54c4fSMartin Matuska } 952caf54c4fSMartin Matuska 953caf54c4fSMartin Matuska for (p = list; (p - list) < list_size; p += strlen(p) + 1) { 954caf54c4fSMartin Matuska if (strncmp(p, "system.", 7) == 0 || 955caf54c4fSMartin Matuska strncmp(p, "xfsroot.", 8) == 0) 956caf54c4fSMartin Matuska continue; 957fd082e96SMartin Matuska setup_xattr(a, entry, p, *fd); 958caf54c4fSMartin Matuska } 959caf54c4fSMartin Matuska 960caf54c4fSMartin Matuska free(list); 961caf54c4fSMartin Matuska return (ARCHIVE_OK); 962caf54c4fSMartin Matuska } 963caf54c4fSMartin Matuska 964caf54c4fSMartin Matuska #elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE && \ 965caf54c4fSMartin Matuska HAVE_DECL_EXTATTR_NAMESPACE_USER 966caf54c4fSMartin Matuska 967caf54c4fSMartin Matuska /* 968caf54c4fSMartin Matuska * FreeBSD extattr interface. 969caf54c4fSMartin Matuska */ 970caf54c4fSMartin Matuska 971caf54c4fSMartin Matuska /* TODO: Implement this. Follow the Linux model above, but 972caf54c4fSMartin Matuska * with FreeBSD-specific system calls, of course. Be careful 973caf54c4fSMartin Matuska * to not include the system extattrs that hold ACLs; we handle 974caf54c4fSMartin Matuska * those separately. 975caf54c4fSMartin Matuska */ 976caf54c4fSMartin Matuska static int 977caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, 978caf54c4fSMartin Matuska int namespace, const char *name, const char *fullname, int fd); 979caf54c4fSMartin Matuska 980caf54c4fSMartin Matuska static int 981caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, 982caf54c4fSMartin Matuska int namespace, const char *name, const char *fullname, int fd) 983caf54c4fSMartin Matuska { 984caf54c4fSMartin Matuska ssize_t size; 985caf54c4fSMartin Matuska void *value = NULL; 986caf54c4fSMartin Matuska const char *accpath; 987caf54c4fSMartin Matuska 988caf54c4fSMartin Matuska accpath = archive_entry_sourcepath(entry); 989caf54c4fSMartin Matuska if (accpath == NULL) 990caf54c4fSMartin Matuska accpath = archive_entry_pathname(entry); 991caf54c4fSMartin Matuska 9926c95142eSMartin Matuska if (fd >= 0) 9936c95142eSMartin Matuska size = extattr_get_fd(fd, namespace, name, NULL, 0); 9946c95142eSMartin Matuska else if (!a->follow_symlinks) 995caf54c4fSMartin Matuska size = extattr_get_link(accpath, namespace, name, NULL, 0); 996caf54c4fSMartin Matuska else 997caf54c4fSMartin Matuska size = extattr_get_file(accpath, namespace, name, NULL, 0); 998caf54c4fSMartin Matuska 999caf54c4fSMartin Matuska if (size == -1) { 1000caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 1001caf54c4fSMartin Matuska "Couldn't query extended attribute"); 1002caf54c4fSMartin Matuska return (ARCHIVE_WARN); 1003caf54c4fSMartin Matuska } 1004caf54c4fSMartin Matuska 1005caf54c4fSMartin Matuska if (size > 0 && (value = malloc(size)) == NULL) { 1006caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 1007caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 1008caf54c4fSMartin Matuska } 1009caf54c4fSMartin Matuska 10106c95142eSMartin Matuska if (fd >= 0) 10116c95142eSMartin Matuska size = extattr_get_fd(fd, namespace, name, value, size); 10126c95142eSMartin Matuska else if (!a->follow_symlinks) 1013caf54c4fSMartin Matuska size = extattr_get_link(accpath, namespace, name, value, size); 1014caf54c4fSMartin Matuska else 1015caf54c4fSMartin Matuska size = extattr_get_file(accpath, namespace, name, value, size); 1016caf54c4fSMartin Matuska 1017caf54c4fSMartin Matuska if (size == -1) { 1018fd082e96SMartin Matuska free(value); 1019caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 1020caf54c4fSMartin Matuska "Couldn't read extended attribute"); 1021caf54c4fSMartin Matuska return (ARCHIVE_WARN); 1022caf54c4fSMartin Matuska } 1023caf54c4fSMartin Matuska 1024caf54c4fSMartin Matuska archive_entry_xattr_add_entry(entry, fullname, value, size); 1025caf54c4fSMartin Matuska 1026caf54c4fSMartin Matuska free(value); 1027caf54c4fSMartin Matuska return (ARCHIVE_OK); 1028caf54c4fSMartin Matuska } 1029caf54c4fSMartin Matuska 1030caf54c4fSMartin Matuska static int 1031caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a, 1032fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 1033caf54c4fSMartin Matuska { 1034caf54c4fSMartin Matuska char buff[512]; 1035caf54c4fSMartin Matuska char *list, *p; 1036caf54c4fSMartin Matuska ssize_t list_size; 1037caf54c4fSMartin Matuska const char *path; 1038caf54c4fSMartin Matuska int namespace = EXTATTR_NAMESPACE_USER; 1039caf54c4fSMartin Matuska 1040caf54c4fSMartin Matuska path = archive_entry_sourcepath(entry); 1041caf54c4fSMartin Matuska if (path == NULL) 1042caf54c4fSMartin Matuska path = archive_entry_pathname(entry); 1043caf54c4fSMartin Matuska 1044fd082e96SMartin Matuska if (*fd < 0 && a->tree != NULL) { 1045fd082e96SMartin Matuska if (a->follow_symlinks || 1046fd082e96SMartin Matuska archive_entry_filetype(entry) != AE_IFLNK) 1047fd082e96SMartin Matuska *fd = a->open_on_current_dir(a->tree, path, 1048fd082e96SMartin Matuska O_RDONLY | O_NONBLOCK); 1049fd082e96SMartin Matuska if (*fd < 0) { 1050fd082e96SMartin Matuska if (a->tree_enter_working_dir(a->tree) != 0) { 1051fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 1052fd082e96SMartin Matuska "Couldn't access %s", path); 1053fd082e96SMartin Matuska return (ARCHIVE_FAILED); 1054fd082e96SMartin Matuska } 1055fd082e96SMartin Matuska } 1056fd082e96SMartin Matuska } 1057fd082e96SMartin Matuska 1058fd082e96SMartin Matuska if (*fd >= 0) 1059fd082e96SMartin Matuska list_size = extattr_list_fd(*fd, namespace, NULL, 0); 10606c95142eSMartin Matuska else if (!a->follow_symlinks) 1061caf54c4fSMartin Matuska list_size = extattr_list_link(path, namespace, NULL, 0); 1062caf54c4fSMartin Matuska else 1063caf54c4fSMartin Matuska list_size = extattr_list_file(path, namespace, NULL, 0); 1064caf54c4fSMartin Matuska 1065caf54c4fSMartin Matuska if (list_size == -1 && errno == EOPNOTSUPP) 1066caf54c4fSMartin Matuska return (ARCHIVE_OK); 1067caf54c4fSMartin Matuska if (list_size == -1) { 1068caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 1069caf54c4fSMartin Matuska "Couldn't list extended attributes"); 1070caf54c4fSMartin Matuska return (ARCHIVE_WARN); 1071caf54c4fSMartin Matuska } 1072caf54c4fSMartin Matuska 1073caf54c4fSMartin Matuska if (list_size == 0) 1074caf54c4fSMartin Matuska return (ARCHIVE_OK); 1075caf54c4fSMartin Matuska 1076caf54c4fSMartin Matuska if ((list = malloc(list_size)) == NULL) { 1077caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, "Out of memory"); 1078caf54c4fSMartin Matuska return (ARCHIVE_FATAL); 1079caf54c4fSMartin Matuska } 1080caf54c4fSMartin Matuska 1081fd082e96SMartin Matuska if (*fd >= 0) 1082fd082e96SMartin Matuska list_size = extattr_list_fd(*fd, namespace, list, list_size); 10836c95142eSMartin Matuska else if (!a->follow_symlinks) 1084caf54c4fSMartin Matuska list_size = extattr_list_link(path, namespace, list, list_size); 1085caf54c4fSMartin Matuska else 1086caf54c4fSMartin Matuska list_size = extattr_list_file(path, namespace, list, list_size); 1087caf54c4fSMartin Matuska 1088caf54c4fSMartin Matuska if (list_size == -1) { 1089caf54c4fSMartin Matuska archive_set_error(&a->archive, errno, 1090caf54c4fSMartin Matuska "Couldn't retrieve extended attributes"); 1091caf54c4fSMartin Matuska free(list); 1092caf54c4fSMartin Matuska return (ARCHIVE_WARN); 1093caf54c4fSMartin Matuska } 1094caf54c4fSMartin Matuska 1095caf54c4fSMartin Matuska p = list; 1096caf54c4fSMartin Matuska while ((p - list) < list_size) { 1097caf54c4fSMartin Matuska size_t len = 255 & (int)*p; 1098caf54c4fSMartin Matuska char *name; 1099caf54c4fSMartin Matuska 1100caf54c4fSMartin Matuska strcpy(buff, "user."); 1101caf54c4fSMartin Matuska name = buff + strlen(buff); 1102caf54c4fSMartin Matuska memcpy(name, p + 1, len); 1103caf54c4fSMartin Matuska name[len] = '\0'; 1104fd082e96SMartin Matuska setup_xattr(a, entry, namespace, name, buff, *fd); 1105caf54c4fSMartin Matuska p += 1 + len; 1106caf54c4fSMartin Matuska } 1107caf54c4fSMartin Matuska 1108caf54c4fSMartin Matuska free(list); 1109caf54c4fSMartin Matuska return (ARCHIVE_OK); 1110caf54c4fSMartin Matuska } 1111caf54c4fSMartin Matuska 1112caf54c4fSMartin Matuska #else 1113caf54c4fSMartin Matuska 1114caf54c4fSMartin Matuska /* 1115caf54c4fSMartin Matuska * Generic (stub) extended attribute support. 1116caf54c4fSMartin Matuska */ 1117caf54c4fSMartin Matuska static int 1118caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a, 1119fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 1120caf54c4fSMartin Matuska { 1121caf54c4fSMartin Matuska (void)a; /* UNUSED */ 1122caf54c4fSMartin Matuska (void)entry; /* UNUSED */ 1123caf54c4fSMartin Matuska (void)fd; /* UNUSED */ 1124caf54c4fSMartin Matuska return (ARCHIVE_OK); 1125caf54c4fSMartin Matuska } 1126caf54c4fSMartin Matuska 1127caf54c4fSMartin Matuska #endif 11286c95142eSMartin Matuska 11296c95142eSMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H) 11306c95142eSMartin Matuska 11316c95142eSMartin Matuska /* 1132d5d08d29SMartin Matuska * Linux FIEMAP sparse interface. 11336c95142eSMartin Matuska * 11346c95142eSMartin Matuska * The FIEMAP ioctl returns an "extent" for each physical allocation 11356c95142eSMartin Matuska * on disk. We need to process those to generate a more compact list 11366c95142eSMartin Matuska * of logical file blocks. We also need to be very careful to use 11376c95142eSMartin Matuska * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes 11386c95142eSMartin Matuska * does not report allocations for newly-written data that hasn't 11396c95142eSMartin Matuska * been synced to disk. 11406c95142eSMartin Matuska * 11416c95142eSMartin Matuska * It's important to return a minimal sparse file list because we want 11426c95142eSMartin Matuska * to not trigger sparse file extensions if we don't have to, since 11436c95142eSMartin Matuska * not all readers support them. 11446c95142eSMartin Matuska */ 11456c95142eSMartin Matuska 11466c95142eSMartin Matuska static int 1147d5d08d29SMartin Matuska setup_sparse_fiemap(struct archive_read_disk *a, 1148fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 11496c95142eSMartin Matuska { 11506c95142eSMartin Matuska char buff[4096]; 11516c95142eSMartin Matuska struct fiemap *fm; 11526c95142eSMartin Matuska struct fiemap_extent *fe; 11536c95142eSMartin Matuska int64_t size; 1154cdf63a70SMartin Matuska int count, do_fiemap, iters; 11556c95142eSMartin Matuska int exit_sts = ARCHIVE_OK; 11566c95142eSMartin Matuska 11576c95142eSMartin Matuska if (archive_entry_filetype(entry) != AE_IFREG 11586c95142eSMartin Matuska || archive_entry_size(entry) <= 0 11596c95142eSMartin Matuska || archive_entry_hardlink(entry) != NULL) 11606c95142eSMartin Matuska return (ARCHIVE_OK); 11616c95142eSMartin Matuska 1162fd082e96SMartin Matuska if (*fd < 0) { 11636c95142eSMartin Matuska const char *path; 11646c95142eSMartin Matuska 11656c95142eSMartin Matuska path = archive_entry_sourcepath(entry); 11666c95142eSMartin Matuska if (path == NULL) 11676c95142eSMartin Matuska path = archive_entry_pathname(entry); 1168fd082e96SMartin Matuska if (a->tree != NULL) 1169fd082e96SMartin Matuska *fd = a->open_on_current_dir(a->tree, path, 1170acc60b03SMartin Matuska O_RDONLY | O_NONBLOCK | O_CLOEXEC); 1171fd082e96SMartin Matuska else 1172acc60b03SMartin Matuska *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); 1173fd082e96SMartin Matuska if (*fd < 0) { 11746c95142eSMartin Matuska archive_set_error(&a->archive, errno, 11756c95142eSMartin Matuska "Can't open `%s'", path); 11766c95142eSMartin Matuska return (ARCHIVE_FAILED); 11776c95142eSMartin Matuska } 1178acc60b03SMartin Matuska __archive_ensure_cloexec_flag(*fd); 11796c95142eSMartin Matuska } 11806c95142eSMartin Matuska 1181acc60b03SMartin Matuska /* Initialize buffer to avoid the error valgrind complains about. */ 1182acc60b03SMartin Matuska memset(buff, 0, sizeof(buff)); 11836c95142eSMartin Matuska count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe); 11846c95142eSMartin Matuska fm = (struct fiemap *)buff; 11856c95142eSMartin Matuska fm->fm_start = 0; 11866c95142eSMartin Matuska fm->fm_length = ~0ULL;; 11876c95142eSMartin Matuska fm->fm_flags = FIEMAP_FLAG_SYNC; 11886c95142eSMartin Matuska fm->fm_extent_count = count; 11896c95142eSMartin Matuska do_fiemap = 1; 11906c95142eSMartin Matuska size = archive_entry_size(entry); 1191cdf63a70SMartin Matuska for (iters = 0; ; ++iters) { 11926c95142eSMartin Matuska int i, r; 11936c95142eSMartin Matuska 1194fd082e96SMartin Matuska r = ioctl(*fd, FS_IOC_FIEMAP, fm); 11956c95142eSMartin Matuska if (r < 0) { 1196fd082e96SMartin Matuska /* When something error happens, it is better we 1197fd082e96SMartin Matuska * should return ARCHIVE_OK because an earlier 1198a2e802b7SMartin Matuska * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */ 1199d5d08d29SMartin Matuska goto exit_setup_sparse_fiemap; 12006c95142eSMartin Matuska } 1201cdf63a70SMartin Matuska if (fm->fm_mapped_extents == 0) { 1202cdf63a70SMartin Matuska if (iters == 0) { 1203cdf63a70SMartin Matuska /* Fully sparse file; insert a zero-length "data" entry */ 1204cdf63a70SMartin Matuska archive_entry_sparse_add_entry(entry, 0, 0); 1205cdf63a70SMartin Matuska } 12066c95142eSMartin Matuska break; 1207cdf63a70SMartin Matuska } 12086c95142eSMartin Matuska fe = fm->fm_extents; 12096c95142eSMartin Matuska for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) { 12106c95142eSMartin Matuska if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) { 12116c95142eSMartin Matuska /* The fe_length of the last block does not 12126c95142eSMartin Matuska * adjust itself to its size files. */ 12136c95142eSMartin Matuska int64_t length = fe->fe_length; 12146c95142eSMartin Matuska if (fe->fe_logical + length > (uint64_t)size) 12156c95142eSMartin Matuska length -= fe->fe_logical + length - size; 12166c95142eSMartin Matuska if (fe->fe_logical == 0 && length == size) { 12176c95142eSMartin Matuska /* This is not sparse. */ 12186c95142eSMartin Matuska do_fiemap = 0; 12196c95142eSMartin Matuska break; 12206c95142eSMartin Matuska } 12216c95142eSMartin Matuska if (length > 0) 12226c95142eSMartin Matuska archive_entry_sparse_add_entry(entry, 12236c95142eSMartin Matuska fe->fe_logical, length); 12246c95142eSMartin Matuska } 12256c95142eSMartin Matuska if (fe->fe_flags & FIEMAP_EXTENT_LAST) 12266c95142eSMartin Matuska do_fiemap = 0; 12276c95142eSMartin Matuska } 12286c95142eSMartin Matuska if (do_fiemap) { 12296c95142eSMartin Matuska fe = fm->fm_extents + fm->fm_mapped_extents -1; 12306c95142eSMartin Matuska fm->fm_start = fe->fe_logical + fe->fe_length; 12316c95142eSMartin Matuska } else 12326c95142eSMartin Matuska break; 12336c95142eSMartin Matuska } 1234d5d08d29SMartin Matuska exit_setup_sparse_fiemap: 12356c95142eSMartin Matuska return (exit_sts); 12366c95142eSMartin Matuska } 12376c95142eSMartin Matuska 1238d5d08d29SMartin Matuska #if !defined(SEEK_HOLE) || !defined(SEEK_DATA) 1239d5d08d29SMartin Matuska static int 1240d5d08d29SMartin Matuska setup_sparse(struct archive_read_disk *a, 1241d5d08d29SMartin Matuska struct archive_entry *entry, int *fd) 1242d5d08d29SMartin Matuska { 1243d5d08d29SMartin Matuska return setup_sparse_fiemap(a, entry, fd); 1244d5d08d29SMartin Matuska } 1245d5d08d29SMartin Matuska #endif 1246d5d08d29SMartin Matuska #endif /* defined(HAVE_LINUX_FIEMAP_H) */ 1247d5d08d29SMartin Matuska 1248d5d08d29SMartin Matuska #if defined(SEEK_HOLE) && defined(SEEK_DATA) 12496c95142eSMartin Matuska 12506c95142eSMartin Matuska /* 1251d5d08d29SMartin Matuska * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris) 12526c95142eSMartin Matuska */ 12536c95142eSMartin Matuska 12546c95142eSMartin Matuska static int 12556c95142eSMartin Matuska setup_sparse(struct archive_read_disk *a, 1256fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 12576c95142eSMartin Matuska { 12586c95142eSMartin Matuska int64_t size; 1259d5d08d29SMartin Matuska off_t initial_off; 1260d5d08d29SMartin Matuska off_t off_s, off_e; 12616c95142eSMartin Matuska int exit_sts = ARCHIVE_OK; 1262cdf63a70SMartin Matuska int check_fully_sparse = 0; 12636c95142eSMartin Matuska 12646c95142eSMartin Matuska if (archive_entry_filetype(entry) != AE_IFREG 12656c95142eSMartin Matuska || archive_entry_size(entry) <= 0 12666c95142eSMartin Matuska || archive_entry_hardlink(entry) != NULL) 12676c95142eSMartin Matuska return (ARCHIVE_OK); 12686c95142eSMartin Matuska 12696c95142eSMartin Matuska /* Does filesystem support the reporting of hole ? */ 1270fd082e96SMartin Matuska if (*fd < 0 && a->tree != NULL) { 1271fd082e96SMartin Matuska const char *path; 1272fd082e96SMartin Matuska 1273fd082e96SMartin Matuska path = archive_entry_sourcepath(entry); 1274fd082e96SMartin Matuska if (path == NULL) 1275fd082e96SMartin Matuska path = archive_entry_pathname(entry); 1276fd082e96SMartin Matuska *fd = a->open_on_current_dir(a->tree, path, 1277fd082e96SMartin Matuska O_RDONLY | O_NONBLOCK); 1278fd082e96SMartin Matuska if (*fd < 0) { 1279fd082e96SMartin Matuska archive_set_error(&a->archive, errno, 1280fd082e96SMartin Matuska "Can't open `%s'", path); 1281fd082e96SMartin Matuska return (ARCHIVE_FAILED); 1282fd082e96SMartin Matuska } 1283fd082e96SMartin Matuska } 1284fd082e96SMartin Matuska 1285fd082e96SMartin Matuska if (*fd >= 0) { 1286d5d08d29SMartin Matuska #ifdef _PC_MIN_HOLE_SIZE 1287fd082e96SMartin Matuska if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0) 12886c95142eSMartin Matuska return (ARCHIVE_OK); 1289d5d08d29SMartin Matuska #endif 1290fd082e96SMartin Matuska initial_off = lseek(*fd, 0, SEEK_CUR); 12916c95142eSMartin Matuska if (initial_off != 0) 1292fd082e96SMartin Matuska lseek(*fd, 0, SEEK_SET); 12936c95142eSMartin Matuska } else { 12946c95142eSMartin Matuska const char *path; 12956c95142eSMartin Matuska 12966c95142eSMartin Matuska path = archive_entry_sourcepath(entry); 12976c95142eSMartin Matuska if (path == NULL) 12986c95142eSMartin Matuska path = archive_entry_pathname(entry); 1299fd082e96SMartin Matuska 1300d5d08d29SMartin Matuska #ifdef _PC_MIN_HOLE_SIZE 13016c95142eSMartin Matuska if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0) 13026c95142eSMartin Matuska return (ARCHIVE_OK); 1303d5d08d29SMartin Matuska #endif 1304acc60b03SMartin Matuska *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); 1305fd082e96SMartin Matuska if (*fd < 0) { 13066c95142eSMartin Matuska archive_set_error(&a->archive, errno, 13076c95142eSMartin Matuska "Can't open `%s'", path); 13086c95142eSMartin Matuska return (ARCHIVE_FAILED); 13096c95142eSMartin Matuska } 1310acc60b03SMartin Matuska __archive_ensure_cloexec_flag(*fd); 13116c95142eSMartin Matuska initial_off = 0; 13126c95142eSMartin Matuska } 13136c95142eSMartin Matuska 1314d5d08d29SMartin Matuska #ifndef _PC_MIN_HOLE_SIZE 1315d5d08d29SMartin Matuska /* Check if the underlying filesystem supports seek hole */ 1316d5d08d29SMartin Matuska off_s = lseek(*fd, 0, SEEK_HOLE); 1317d5d08d29SMartin Matuska if (off_s < 0) 1318d5d08d29SMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H) 1319d5d08d29SMartin Matuska return setup_sparse_fiemap(a, entry, fd); 1320d5d08d29SMartin Matuska #else 1321d5d08d29SMartin Matuska goto exit_setup_sparse; 1322d5d08d29SMartin Matuska #endif 1323d5d08d29SMartin Matuska else if (off_s > 0) 1324d5d08d29SMartin Matuska lseek(*fd, 0, SEEK_SET); 1325d5d08d29SMartin Matuska #endif 1326d5d08d29SMartin Matuska 13276c95142eSMartin Matuska off_s = 0; 13286c95142eSMartin Matuska size = archive_entry_size(entry); 13296c95142eSMartin Matuska while (off_s < size) { 1330fd082e96SMartin Matuska off_s = lseek(*fd, off_s, SEEK_DATA); 13316c95142eSMartin Matuska if (off_s == (off_t)-1) { 1332cdf63a70SMartin Matuska if (errno == ENXIO) { 1333cdf63a70SMartin Matuska /* no more hole */ 1334cdf63a70SMartin Matuska if (archive_entry_sparse_count(entry) == 0) { 1335cdf63a70SMartin Matuska /* Potentially a fully-sparse file. */ 1336cdf63a70SMartin Matuska check_fully_sparse = 1; 1337cdf63a70SMartin Matuska } 1338cdf63a70SMartin Matuska break; 1339cdf63a70SMartin Matuska } 13406c95142eSMartin Matuska archive_set_error(&a->archive, errno, 13416c95142eSMartin Matuska "lseek(SEEK_HOLE) failed"); 13426c95142eSMartin Matuska exit_sts = ARCHIVE_FAILED; 13436c95142eSMartin Matuska goto exit_setup_sparse; 13446c95142eSMartin Matuska } 1345fd082e96SMartin Matuska off_e = lseek(*fd, off_s, SEEK_HOLE); 1346fd082e96SMartin Matuska if (off_e == (off_t)-1) { 13476c95142eSMartin Matuska if (errno == ENXIO) { 1348fd082e96SMartin Matuska off_e = lseek(*fd, 0, SEEK_END); 13496c95142eSMartin Matuska if (off_e != (off_t)-1) 13506c95142eSMartin Matuska break;/* no more data */ 13516c95142eSMartin Matuska } 13526c95142eSMartin Matuska archive_set_error(&a->archive, errno, 13536c95142eSMartin Matuska "lseek(SEEK_DATA) failed"); 13546c95142eSMartin Matuska exit_sts = ARCHIVE_FAILED; 13556c95142eSMartin Matuska goto exit_setup_sparse; 13566c95142eSMartin Matuska } 13576c95142eSMartin Matuska if (off_s == 0 && off_e == size) 1358a2e802b7SMartin Matuska break;/* This is not sparse. */ 13596c95142eSMartin Matuska archive_entry_sparse_add_entry(entry, off_s, 13606c95142eSMartin Matuska off_e - off_s); 13616c95142eSMartin Matuska off_s = off_e; 13626c95142eSMartin Matuska } 1363cdf63a70SMartin Matuska 1364cdf63a70SMartin Matuska if (check_fully_sparse) { 1365cdf63a70SMartin Matuska if (lseek(*fd, 0, SEEK_HOLE) == 0 && 1366cdf63a70SMartin Matuska lseek(*fd, 0, SEEK_END) == size) { 1367cdf63a70SMartin Matuska /* Fully sparse file; insert a zero-length "data" entry */ 1368cdf63a70SMartin Matuska archive_entry_sparse_add_entry(entry, 0, 0); 1369cdf63a70SMartin Matuska } 1370cdf63a70SMartin Matuska } 13716c95142eSMartin Matuska exit_setup_sparse: 1372fd082e96SMartin Matuska lseek(*fd, initial_off, SEEK_SET); 13736c95142eSMartin Matuska return (exit_sts); 13746c95142eSMartin Matuska } 13756c95142eSMartin Matuska 1376d5d08d29SMartin Matuska #elif !defined(HAVE_LINUX_FIEMAP_H) 13776c95142eSMartin Matuska 13786c95142eSMartin Matuska /* 13796c95142eSMartin Matuska * Generic (stub) sparse support. 13806c95142eSMartin Matuska */ 13816c95142eSMartin Matuska static int 13826c95142eSMartin Matuska setup_sparse(struct archive_read_disk *a, 1383fd082e96SMartin Matuska struct archive_entry *entry, int *fd) 13846c95142eSMartin Matuska { 13856c95142eSMartin Matuska (void)a; /* UNUSED */ 13866c95142eSMartin Matuska (void)entry; /* UNUSED */ 13876c95142eSMartin Matuska (void)fd; /* UNUSED */ 13886c95142eSMartin Matuska return (ARCHIVE_OK); 13896c95142eSMartin Matuska } 13906c95142eSMartin Matuska 13916c95142eSMartin Matuska #endif 13926c95142eSMartin Matuska 13936c95142eSMartin Matuska #endif /* !defined(_WIN32) || defined(__CYGWIN__) */ 13946c95142eSMartin Matuska 1395