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, &copyfile_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