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