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"
29caf54c4fSMartin Matuska 
306c95142eSMartin Matuska /* This is the tree-walking code for POSIX systems. */
316c95142eSMartin Matuska #if !defined(_WIN32) || defined(__CYGWIN__)
326c95142eSMartin Matuska 
33caf54c4fSMartin Matuska #ifdef HAVE_SYS_TYPES_H
34caf54c4fSMartin Matuska #include <sys/types.h>
35caf54c4fSMartin Matuska #endif
36caf54c4fSMartin Matuska #ifdef HAVE_SYS_EXTATTR_H
37caf54c4fSMartin Matuska #include <sys/extattr.h>
38caf54c4fSMartin Matuska #endif
396c95142eSMartin Matuska #ifdef HAVE_SYS_IOCTL_H
406c95142eSMartin Matuska #include <sys/ioctl.h>
416c95142eSMartin Matuska #endif
42caf54c4fSMartin Matuska #ifdef HAVE_SYS_PARAM_H
43caf54c4fSMartin Matuska #include <sys/param.h>
44caf54c4fSMartin Matuska #endif
45caf54c4fSMartin Matuska #ifdef HAVE_SYS_STAT_H
46caf54c4fSMartin Matuska #include <sys/stat.h>
47caf54c4fSMartin Matuska #endif
48acc60b03SMartin Matuska #if defined(HAVE_SYS_XATTR_H)
49caf54c4fSMartin Matuska #include <sys/xattr.h>
50acc60b03SMartin Matuska #elif defined(HAVE_ATTR_XATTR_H)
51acc60b03SMartin Matuska #include <attr/xattr.h>
52caf54c4fSMartin Matuska #endif
536c95142eSMartin Matuska #ifdef HAVE_SYS_EA_H
546c95142eSMartin Matuska #include <sys/ea.h>
556c95142eSMartin Matuska #endif
566c95142eSMartin Matuska #ifdef HAVE_COPYFILE_H
576c95142eSMartin Matuska #include <copyfile.h>
586c95142eSMartin Matuska #endif
59caf54c4fSMartin Matuska #ifdef HAVE_ERRNO_H
60caf54c4fSMartin Matuska #include <errno.h>
61caf54c4fSMartin Matuska #endif
626c95142eSMartin Matuska #ifdef HAVE_FCNTL_H
636c95142eSMartin Matuska #include <fcntl.h>
646c95142eSMartin Matuska #endif
65caf54c4fSMartin Matuska #ifdef HAVE_LIMITS_H
66caf54c4fSMartin Matuska #include <limits.h>
67caf54c4fSMartin Matuska #endif
68fd082e96SMartin Matuska #ifdef HAVE_LINUX_TYPES_H
69fd082e96SMartin Matuska #include <linux/types.h>
70fd082e96SMartin Matuska #endif
716c95142eSMartin Matuska #ifdef HAVE_LINUX_FIEMAP_H
726c95142eSMartin Matuska #include <linux/fiemap.h>
736c95142eSMartin Matuska #endif
746c95142eSMartin Matuska #ifdef HAVE_LINUX_FS_H
756c95142eSMartin Matuska #include <linux/fs.h>
766c95142eSMartin Matuska #endif
776c95142eSMartin Matuska /*
786c95142eSMartin Matuska  * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
796c95142eSMartin Matuska  * As the include guards don't agree, the order of include is important.
806c95142eSMartin Matuska  */
816c95142eSMartin Matuska #ifdef HAVE_LINUX_EXT2_FS_H
826c95142eSMartin Matuska #include <linux/ext2_fs.h>      /* for Linux file flags */
836c95142eSMartin Matuska #endif
846c95142eSMartin Matuska #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
856c95142eSMartin Matuska #include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
866c95142eSMartin Matuska #endif
876c95142eSMartin Matuska #ifdef HAVE_PATHS_H
886c95142eSMartin Matuska #include <paths.h>
896c95142eSMartin Matuska #endif
906c95142eSMartin Matuska #ifdef HAVE_UNISTD_H
916c95142eSMartin Matuska #include <unistd.h>
92caf54c4fSMartin Matuska #endif
93caf54c4fSMartin Matuska 
94caf54c4fSMartin Matuska #include "archive.h"
95caf54c4fSMartin Matuska #include "archive_entry.h"
96caf54c4fSMartin Matuska #include "archive_private.h"
97caf54c4fSMartin Matuska #include "archive_read_disk_private.h"
98caf54c4fSMartin Matuska 
99acc60b03SMartin Matuska #ifndef O_CLOEXEC
100acc60b03SMartin Matuska #define O_CLOEXEC	0
101acc60b03SMartin Matuska #endif
102acc60b03SMartin Matuska 
1036c95142eSMartin Matuska static int setup_mac_metadata(struct archive_read_disk *,
104fd082e96SMartin Matuska     struct archive_entry *, int *fd);
105c3afd20fSMartin Matuska #ifdef ARCHIVE_XATTR_FREEBSD
106c3afd20fSMartin Matuska static int setup_xattrs_namespace(struct archive_read_disk *,
107c3afd20fSMartin Matuska     struct archive_entry *, int *, int);
108c3afd20fSMartin Matuska #endif
109caf54c4fSMartin Matuska static int setup_xattrs(struct archive_read_disk *,
110fd082e96SMartin Matuska     struct archive_entry *, int *fd);
1116c95142eSMartin Matuska static int setup_sparse(struct archive_read_disk *,
112fd082e96SMartin Matuska     struct archive_entry *, int *fd);
113d5d08d29SMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H)
114d5d08d29SMartin Matuska static int setup_sparse_fiemap(struct archive_read_disk *,
115d5d08d29SMartin Matuska     struct archive_entry *, int *fd);
116d5d08d29SMartin Matuska #endif
117caf54c4fSMartin Matuska 
1184657548dSMartin Matuska #if !ARCHIVE_ACL_SUPPORT
1194657548dSMartin Matuska int
archive_read_disk_entry_setup_acls(struct archive_read_disk * a,struct archive_entry * entry,int * fd)1204657548dSMartin Matuska archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
1214657548dSMartin Matuska     struct archive_entry *entry, int *fd)
1224657548dSMartin Matuska {
1234657548dSMartin Matuska 	(void)a;      /* UNUSED */
1244657548dSMartin Matuska 	(void)entry;  /* UNUSED */
1254657548dSMartin Matuska 	(void)fd;     /* UNUSED */
1264657548dSMartin Matuska 	return (ARCHIVE_OK);
1274657548dSMartin Matuska }
1284657548dSMartin Matuska #endif
1294657548dSMartin Matuska 
1304657548dSMartin Matuska /*
1314657548dSMartin Matuska  * Enter working directory and return working pathname of archive_entry.
1324657548dSMartin Matuska  * If a pointer to an integer is provided and its value is below zero
1335c831a5bSMartin Matuska  * open a file descriptor on this pathname.
1344657548dSMartin Matuska  */
1354657548dSMartin Matuska const char *
archive_read_disk_entry_setup_path(struct archive_read_disk * a,struct archive_entry * entry,int * fd)1364657548dSMartin Matuska archive_read_disk_entry_setup_path(struct archive_read_disk *a,
1374657548dSMartin Matuska     struct archive_entry *entry, int *fd)
1384657548dSMartin Matuska {
1394657548dSMartin Matuska 	const char *path;
1404657548dSMartin Matuska 
1414657548dSMartin Matuska 	path = archive_entry_sourcepath(entry);
1424657548dSMartin Matuska 
1434657548dSMartin Matuska 	if (path == NULL || (a->tree != NULL &&
1444657548dSMartin Matuska 	    a->tree_enter_working_dir(a->tree) != 0))
1454657548dSMartin Matuska 		path = archive_entry_pathname(entry);
1464657548dSMartin Matuska 	if (path == NULL) {
1474657548dSMartin Matuska 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
1484657548dSMartin Matuska 		   "Couldn't determine path");
1494657548dSMartin Matuska 	} else if (fd != NULL && *fd < 0 && a->tree != NULL &&
1504657548dSMartin Matuska 	    (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)) {
1514657548dSMartin Matuska 		*fd = a->open_on_current_dir(a->tree, path,
1524657548dSMartin Matuska 		    O_RDONLY | O_NONBLOCK);
1534657548dSMartin Matuska 	}
1544657548dSMartin Matuska 	return (path);
1554657548dSMartin Matuska }
1564657548dSMartin Matuska 
157caf54c4fSMartin Matuska int
archive_read_disk_entry_from_file(struct archive * _a,struct archive_entry * entry,int fd,const struct stat * st)158caf54c4fSMartin Matuska archive_read_disk_entry_from_file(struct archive *_a,
159caf54c4fSMartin Matuska     struct archive_entry *entry,
1606c95142eSMartin Matuska     int fd,
1616c95142eSMartin Matuska     const struct stat *st)
162caf54c4fSMartin Matuska {
163caf54c4fSMartin Matuska 	struct archive_read_disk *a = (struct archive_read_disk *)_a;
164caf54c4fSMartin Matuska 	const char *path, *name;
165caf54c4fSMartin Matuska 	struct stat s;
166caf54c4fSMartin Matuska 	int initial_fd = fd;
167caf54c4fSMartin Matuska 	int r, r1;
168caf54c4fSMartin Matuska 
169df422cb4SMartin Matuska 	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
170df422cb4SMartin Matuska 		"archive_read_disk_entry_from_file");
171df422cb4SMartin Matuska 
172caf54c4fSMartin Matuska 	archive_clear_error(_a);
173caf54c4fSMartin Matuska 	path = archive_entry_sourcepath(entry);
174caf54c4fSMartin Matuska 	if (path == NULL)
175caf54c4fSMartin Matuska 		path = archive_entry_pathname(entry);
176caf54c4fSMartin Matuska 
1776c95142eSMartin Matuska 	if (a->tree == NULL) {
178caf54c4fSMartin Matuska 		if (st == NULL) {
179caf54c4fSMartin Matuska #if HAVE_FSTAT
180caf54c4fSMartin Matuska 			if (fd >= 0) {
181caf54c4fSMartin Matuska 				if (fstat(fd, &s) != 0) {
182caf54c4fSMartin Matuska 					archive_set_error(&a->archive, errno,
183caf54c4fSMartin Matuska 					    "Can't fstat");
184caf54c4fSMartin Matuska 					return (ARCHIVE_FAILED);
185caf54c4fSMartin Matuska 				}
186caf54c4fSMartin Matuska 			} else
187caf54c4fSMartin Matuska #endif
188caf54c4fSMartin Matuska #if HAVE_LSTAT
189caf54c4fSMartin Matuska 			if (!a->follow_symlinks) {
190caf54c4fSMartin Matuska 				if (lstat(path, &s) != 0) {
191caf54c4fSMartin Matuska 					archive_set_error(&a->archive, errno,
192caf54c4fSMartin Matuska 					    "Can't lstat %s", path);
193caf54c4fSMartin Matuska 					return (ARCHIVE_FAILED);
194caf54c4fSMartin Matuska 				}
195caf54c4fSMartin Matuska 			} else
196caf54c4fSMartin Matuska #endif
19752c2bb75SMartin Matuska 			if (la_stat(path, &s) != 0) {
198caf54c4fSMartin Matuska 				archive_set_error(&a->archive, errno,
1996c95142eSMartin Matuska 				    "Can't stat %s", path);
200caf54c4fSMartin Matuska 				return (ARCHIVE_FAILED);
201caf54c4fSMartin Matuska 			}
202caf54c4fSMartin Matuska 			st = &s;
203caf54c4fSMartin Matuska 		}
204caf54c4fSMartin Matuska 		archive_entry_copy_stat(entry, st);
20510ed66fdSMartin Matuska 	}
20610ed66fdSMartin Matuska 
207caf54c4fSMartin Matuska 	/* Lookup uname/gname */
208caf54c4fSMartin Matuska 	name = archive_read_disk_uname(_a, archive_entry_uid(entry));
209caf54c4fSMartin Matuska 	if (name != NULL)
210caf54c4fSMartin Matuska 		archive_entry_copy_uname(entry, name);
211caf54c4fSMartin Matuska 	name = archive_read_disk_gname(_a, archive_entry_gid(entry));
212caf54c4fSMartin Matuska 	if (name != NULL)
213caf54c4fSMartin Matuska 		archive_entry_copy_gname(entry, name);
214caf54c4fSMartin Matuska 
215caf54c4fSMartin Matuska #ifdef HAVE_STRUCT_STAT_ST_FLAGS
216caf54c4fSMartin Matuska 	/* On FreeBSD, we get flags for free with the stat. */
217caf54c4fSMartin Matuska 	/* TODO: Does this belong in copy_stat()? */
21864287048SMartin Matuska 	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0)
219caf54c4fSMartin Matuska 		archive_entry_set_fflags(entry, st->st_flags, 0);
220caf54c4fSMartin Matuska #endif
221caf54c4fSMartin Matuska 
22264287048SMartin Matuska #if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
22364287048SMartin Matuska     (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
2246c95142eSMartin Matuska 	/* Linux requires an extra ioctl to pull the flags.  Although
2256c95142eSMartin Matuska 	 * this is an extra step, it has a nice side-effect: We get an
2266c95142eSMartin Matuska 	 * open file descriptor which we can use in the subsequent lookups. */
22764287048SMartin Matuska 	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 &&
22864287048SMartin Matuska 	    (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
229fd082e96SMartin Matuska 		if (fd < 0) {
230fd082e96SMartin Matuska 			if (a->tree != NULL)
231fd082e96SMartin Matuska 				fd = a->open_on_current_dir(a->tree, path,
232acc60b03SMartin Matuska 					O_RDONLY | O_NONBLOCK | O_CLOEXEC);
233fd082e96SMartin Matuska 			else
234acc60b03SMartin Matuska 				fd = open(path, O_RDONLY | O_NONBLOCK |
235acc60b03SMartin Matuska 						O_CLOEXEC);
236acc60b03SMartin Matuska 			__archive_ensure_cloexec_flag(fd);
237fd082e96SMartin Matuska 		}
2386c95142eSMartin Matuska 		if (fd >= 0) {
239acc60b03SMartin Matuska 			int stflags;
24064287048SMartin Matuska 			r = ioctl(fd,
24164287048SMartin Matuska #if defined(FS_IOC_GETFLAGS)
24264287048SMartin Matuska 			    FS_IOC_GETFLAGS,
24364287048SMartin Matuska #else
24464287048SMartin Matuska 			    EXT2_IOC_GETFLAGS,
24564287048SMartin Matuska #endif
24664287048SMartin Matuska 			    &stflags);
2476c95142eSMartin Matuska 			if (r == 0 && stflags != 0)
2486c95142eSMartin Matuska 				archive_entry_set_fflags(entry, stflags, 0);
2496c95142eSMartin Matuska 		}
2506c95142eSMartin Matuska 	}
2516c95142eSMartin Matuska #endif
2526c95142eSMartin Matuska 
2536c95142eSMartin Matuska #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT)
254caf54c4fSMartin Matuska 	if (S_ISLNK(st->st_mode)) {
25579085fd3SMartin Matuska 		size_t linkbuffer_len = st->st_size;
2566c95142eSMartin Matuska 		char *linkbuffer;
2576c95142eSMartin Matuska 		int lnklen;
2586c95142eSMartin Matuska 
25979085fd3SMartin Matuska 		linkbuffer = malloc(linkbuffer_len + 1);
2606c95142eSMartin Matuska 		if (linkbuffer == NULL) {
2616c95142eSMartin Matuska 			archive_set_error(&a->archive, ENOMEM,
2626c95142eSMartin Matuska 			    "Couldn't read link data");
2636c95142eSMartin Matuska 			return (ARCHIVE_FAILED);
2646c95142eSMartin Matuska 		}
265fd082e96SMartin Matuska 		if (a->tree != NULL) {
2666c95142eSMartin Matuska #ifdef HAVE_READLINKAT
267fd082e96SMartin Matuska 			lnklen = readlinkat(a->tree_current_dir_fd(a->tree),
268fd082e96SMartin Matuska 			    path, linkbuffer, linkbuffer_len);
269fd082e96SMartin Matuska #else
270fd082e96SMartin Matuska 			if (a->tree_enter_working_dir(a->tree) != 0) {
271fd082e96SMartin Matuska 				archive_set_error(&a->archive, errno,
272fd082e96SMartin Matuska 				    "Couldn't read link data");
273fd082e96SMartin Matuska 				free(linkbuffer);
274fd082e96SMartin Matuska 				return (ARCHIVE_FAILED);
275fd082e96SMartin Matuska 			}
276fd082e96SMartin Matuska 			lnklen = readlink(path, linkbuffer, linkbuffer_len);
2776c95142eSMartin Matuska #endif /* HAVE_READLINKAT */
278fd082e96SMartin Matuska 		} else
2796c95142eSMartin Matuska 			lnklen = readlink(path, linkbuffer, linkbuffer_len);
280caf54c4fSMartin Matuska 		if (lnklen < 0) {
281caf54c4fSMartin Matuska 			archive_set_error(&a->archive, errno,
282caf54c4fSMartin Matuska 			    "Couldn't read link data");
2836c95142eSMartin Matuska 			free(linkbuffer);
284caf54c4fSMartin Matuska 			return (ARCHIVE_FAILED);
285caf54c4fSMartin Matuska 		}
28679085fd3SMartin Matuska 		linkbuffer[lnklen] = '\0';
287caf54c4fSMartin Matuska 		archive_entry_set_symlink(entry, linkbuffer);
2886c95142eSMartin Matuska 		free(linkbuffer);
289caf54c4fSMartin Matuska 	}
2906c95142eSMartin Matuska #endif /* HAVE_READLINK || HAVE_READLINKAT */
291caf54c4fSMartin Matuska 
29264287048SMartin Matuska 	r = 0;
29364287048SMartin Matuska 	if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0)
2944657548dSMartin Matuska 		r = archive_read_disk_entry_setup_acls(a, entry, &fd);
29564287048SMartin Matuska 	if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) {
296fd082e96SMartin Matuska 		r1 = setup_xattrs(a, entry, &fd);
297caf54c4fSMartin Matuska 		if (r1 < r)
298caf54c4fSMartin Matuska 			r = r1;
299cdf63a70SMartin Matuska 	}
30064287048SMartin Matuska 	if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {
301fd082e96SMartin Matuska 		r1 = setup_mac_metadata(a, entry, &fd);
3026c95142eSMartin Matuska 		if (r1 < r)
3036c95142eSMartin Matuska 			r = r1;
304fd082e96SMartin Matuska 	}
305833a452eSMartin Matuska 	if ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) {
306fd082e96SMartin Matuska 		r1 = setup_sparse(a, entry, &fd);
3076c95142eSMartin Matuska 		if (r1 < r)
3086c95142eSMartin Matuska 			r = r1;
309833a452eSMartin Matuska 	}
3106c95142eSMartin Matuska 
311caf54c4fSMartin Matuska 	/* If we opened the file earlier in this function, close it. */
312caf54c4fSMartin Matuska 	if (initial_fd != fd)
313caf54c4fSMartin Matuska 		close(fd);
314caf54c4fSMartin Matuska 	return (r);
315caf54c4fSMartin Matuska }
316caf54c4fSMartin Matuska 
3176c95142eSMartin Matuska #if defined(__APPLE__) && defined(HAVE_COPYFILE_H)
3186c95142eSMartin Matuska /*
3196c95142eSMartin Matuska  * The Mac OS "copyfile()" API copies the extended metadata for a
3206c95142eSMartin Matuska  * file into a separate file in AppleDouble format (see RFC 1740).
3216c95142eSMartin Matuska  *
3226c95142eSMartin Matuska  * Mac OS tar and cpio implementations store this extended
3236c95142eSMartin Matuska  * metadata as a separate entry just before the regular entry
3246c95142eSMartin Matuska  * with a "._" prefix added to the filename.
3256c95142eSMartin Matuska  *
3266c95142eSMartin Matuska  * Note that this is currently done unconditionally; the tar program has
3276c95142eSMartin Matuska  * an option to discard this information before the archive is written.
3286c95142eSMartin Matuska  *
3296c95142eSMartin Matuska  * TODO: If there's a failure, report it and return ARCHIVE_WARN.
3306c95142eSMartin Matuska  */
3316c95142eSMartin Matuska static int
setup_mac_metadata(struct archive_read_disk * a,struct archive_entry * entry,int * fd)3326c95142eSMartin Matuska setup_mac_metadata(struct archive_read_disk *a,
333fd082e96SMartin Matuska     struct archive_entry *entry, int *fd)
3346c95142eSMartin Matuska {
3356c95142eSMartin Matuska 	int tempfd = -1;
3366c95142eSMartin Matuska 	int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;
3376c95142eSMartin Matuska 	struct stat copyfile_stat;
3386c95142eSMartin Matuska 	int ret = ARCHIVE_OK;
339acc60b03SMartin Matuska 	void *buff = NULL;
3406c95142eSMartin Matuska 	int have_attrs;
341acc60b03SMartin Matuska 	const char *name, *tempdir;
342acc60b03SMartin Matuska 	struct archive_string tempfile;
3436c95142eSMartin Matuska 
344fd082e96SMartin Matuska 	(void)fd; /* UNUSED */
3454657548dSMartin Matuska 
3464657548dSMartin Matuska 	name = archive_read_disk_entry_setup_path(a, entry, NULL);
3476c95142eSMartin Matuska 	if (name == NULL)
3486c95142eSMartin Matuska 		return (ARCHIVE_WARN);
3496c95142eSMartin Matuska 
3506c95142eSMartin Matuska 	/* Short-circuit if there's nothing to do. */
3516c95142eSMartin Matuska 	have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
3526c95142eSMartin Matuska 	if (have_attrs == -1) {
3536c95142eSMartin Matuska 		archive_set_error(&a->archive, errno,
3546c95142eSMartin Matuska 			"Could not check extended attributes");
3556c95142eSMartin Matuska 		return (ARCHIVE_WARN);
3566c95142eSMartin Matuska 	}
3576c95142eSMartin Matuska 	if (have_attrs == 0)
3586c95142eSMartin Matuska 		return (ARCHIVE_OK);
3596c95142eSMartin Matuska 
3606c95142eSMartin Matuska 	tempdir = NULL;
3616c95142eSMartin Matuska 	if (issetugid() == 0)
3626c95142eSMartin Matuska 		tempdir = getenv("TMPDIR");
3636c95142eSMartin Matuska 	if (tempdir == NULL)
3646c95142eSMartin Matuska 		tempdir = _PATH_TMP;
365acc60b03SMartin Matuska 	archive_string_init(&tempfile);
366acc60b03SMartin Matuska 	archive_strcpy(&tempfile, tempdir);
367acc60b03SMartin Matuska 	archive_strcat(&tempfile, "tar.md.XXXXXX");
368acc60b03SMartin Matuska 	tempfd = mkstemp(tempfile.s);
369acc60b03SMartin Matuska 	if (tempfd < 0) {
370acc60b03SMartin Matuska 		archive_set_error(&a->archive, errno,
371acc60b03SMartin Matuska 		    "Could not open extended attribute file");
372acc60b03SMartin Matuska 		ret = ARCHIVE_WARN;
373acc60b03SMartin Matuska 		goto cleanup;
374acc60b03SMartin Matuska 	}
375acc60b03SMartin Matuska 	__archive_ensure_cloexec_flag(tempfd);
3766c95142eSMartin Matuska 
3776c95142eSMartin Matuska 	/* XXX I wish copyfile() could pack directly to a memory
3786c95142eSMartin Matuska 	 * buffer; that would avoid the temp file here.  For that
3796c95142eSMartin Matuska 	 * matter, it would be nice if fcopyfile() actually worked,
3806c95142eSMartin Matuska 	 * that would reduce the many open/close races here. */
381acc60b03SMartin Matuska 	if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) {
3826c95142eSMartin Matuska 		archive_set_error(&a->archive, errno,
3836c95142eSMartin Matuska 		    "Could not pack extended attributes");
3846c95142eSMartin Matuska 		ret = ARCHIVE_WARN;
3856c95142eSMartin Matuska 		goto cleanup;
3866c95142eSMartin Matuska 	}
3876c95142eSMartin Matuska 	if (fstat(tempfd, &copyfile_stat)) {
3886c95142eSMartin Matuska 		archive_set_error(&a->archive, errno,
3896c95142eSMartin Matuska 		    "Could not check size of extended attributes");
3906c95142eSMartin Matuska 		ret = ARCHIVE_WARN;
3916c95142eSMartin Matuska 		goto cleanup;
3926c95142eSMartin Matuska 	}
3936c95142eSMartin Matuska 	buff = malloc(copyfile_stat.st_size);
3946c95142eSMartin Matuska 	if (buff == NULL) {
3956c95142eSMartin Matuska 		archive_set_error(&a->archive, errno,
3966c95142eSMartin Matuska 		    "Could not allocate memory for extended attributes");
3976c95142eSMartin Matuska 		ret = ARCHIVE_WARN;
3986c95142eSMartin Matuska 		goto cleanup;
3996c95142eSMartin Matuska 	}
4006c95142eSMartin Matuska 	if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) {
4016c95142eSMartin Matuska 		archive_set_error(&a->archive, errno,
4026c95142eSMartin Matuska 		    "Could not read extended attributes into memory");
4036c95142eSMartin Matuska 		ret = ARCHIVE_WARN;
4046c95142eSMartin Matuska 		goto cleanup;
4056c95142eSMartin Matuska 	}
4066c95142eSMartin Matuska 	archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size);
4076c95142eSMartin Matuska 
4086c95142eSMartin Matuska cleanup:
409acc60b03SMartin Matuska 	if (tempfd >= 0) {
4106c95142eSMartin Matuska 		close(tempfd);
411acc60b03SMartin Matuska 		unlink(tempfile.s);
412acc60b03SMartin Matuska 	}
413acc60b03SMartin Matuska 	archive_string_free(&tempfile);
414acc60b03SMartin Matuska 	free(buff);
4156c95142eSMartin Matuska 	return (ret);
4166c95142eSMartin Matuska }
4176c95142eSMartin Matuska 
4186c95142eSMartin Matuska #else
4196c95142eSMartin Matuska 
4206c95142eSMartin Matuska /*
4216c95142eSMartin Matuska  * Stub implementation for non-Mac systems.
4226c95142eSMartin Matuska  */
4236c95142eSMartin Matuska static int
setup_mac_metadata(struct archive_read_disk * a,struct archive_entry * entry,int * fd)4246c95142eSMartin Matuska setup_mac_metadata(struct archive_read_disk *a,
425fd082e96SMartin Matuska     struct archive_entry *entry, int *fd)
4266c95142eSMartin Matuska {
4276c95142eSMartin Matuska 	(void)a; /* UNUSED */
4286c95142eSMartin Matuska 	(void)entry; /* UNUSED */
4296c95142eSMartin Matuska 	(void)fd; /* UNUSED */
4306c95142eSMartin Matuska 	return (ARCHIVE_OK);
4316c95142eSMartin Matuska }
4326c95142eSMartin Matuska #endif
4336c95142eSMartin Matuska 
434e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
435caf54c4fSMartin Matuska 
436caf54c4fSMartin Matuska /*
437e46d4714SMartin Matuska  * Linux, Darwin and AIX extended attribute support.
438caf54c4fSMartin Matuska  *
439caf54c4fSMartin Matuska  * TODO:  By using a stack-allocated buffer for the first
440caf54c4fSMartin Matuska  * call to getxattr(), we might be able to avoid the second
441caf54c4fSMartin Matuska  * call entirely.  We only need the second call if the
442caf54c4fSMartin Matuska  * stack-allocated buffer is too small.  But a modest buffer
443caf54c4fSMartin Matuska  * of 1024 bytes or so will often be big enough.  Same applies
444caf54c4fSMartin Matuska  * to listxattr().
445caf54c4fSMartin Matuska  */
446caf54c4fSMartin Matuska 
447caf54c4fSMartin Matuska 
448caf54c4fSMartin Matuska static int
setup_xattr(struct archive_read_disk * a,struct archive_entry * entry,const char * name,int fd,const char * accpath)449caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a,
45064287048SMartin Matuska     struct archive_entry *entry, const char *name, int fd, const char *accpath)
451caf54c4fSMartin Matuska {
452caf54c4fSMartin Matuska 	ssize_t size;
453caf54c4fSMartin Matuska 	void *value = NULL;
454caf54c4fSMartin Matuska 
455e46d4714SMartin Matuska 
456e46d4714SMartin Matuska 	if (fd >= 0) {
457e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
4586c95142eSMartin Matuska 		size = fgetxattr(fd, name, NULL, 0);
459e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
460e46d4714SMartin Matuska 		size = fgetxattr(fd, name, NULL, 0, 0, 0);
461e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
4626c95142eSMartin Matuska 		size = fgetea(fd, name, NULL, 0);
463e46d4714SMartin Matuska #endif
464e46d4714SMartin Matuska 	} else if (!a->follow_symlinks) {
465e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
466e46d4714SMartin Matuska 		size = lgetxattr(accpath, name, NULL, 0);
467e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
468e46d4714SMartin Matuska 		size = getxattr(accpath, name, NULL, 0, 0, XATTR_NOFOLLOW);
469e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
4706c95142eSMartin Matuska 		size = lgetea(accpath, name, NULL, 0);
471e46d4714SMartin Matuska #endif
472e46d4714SMartin Matuska 	} else {
473e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
474e46d4714SMartin Matuska 		size = getxattr(accpath, name, NULL, 0);
475e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
476e46d4714SMartin Matuska 		size = getxattr(accpath, name, NULL, 0, 0, 0);
477e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
4786c95142eSMartin Matuska 		size = getea(accpath, name, NULL, 0);
4796c95142eSMartin Matuska #endif
480e46d4714SMartin Matuska 	}
481caf54c4fSMartin Matuska 
482caf54c4fSMartin Matuska 	if (size == -1) {
483caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno,
484caf54c4fSMartin Matuska 		    "Couldn't query extended attribute");
485caf54c4fSMartin Matuska 		return (ARCHIVE_WARN);
486caf54c4fSMartin Matuska 	}
487caf54c4fSMartin Matuska 
488caf54c4fSMartin Matuska 	if (size > 0 && (value = malloc(size)) == NULL) {
489caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno, "Out of memory");
490caf54c4fSMartin Matuska 		return (ARCHIVE_FATAL);
491caf54c4fSMartin Matuska 	}
492caf54c4fSMartin Matuska 
493e46d4714SMartin Matuska 
494e46d4714SMartin Matuska 	if (fd >= 0) {
495e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
4966c95142eSMartin Matuska 		size = fgetxattr(fd, name, value, size);
497e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
498e46d4714SMartin Matuska 		size = fgetxattr(fd, name, value, size, 0, 0);
499e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
5006c95142eSMartin Matuska 		size = fgetea(fd, name, value, size);
501e46d4714SMartin Matuska #endif
502e46d4714SMartin Matuska 	} else if (!a->follow_symlinks) {
503e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
504e46d4714SMartin Matuska 		size = lgetxattr(accpath, name, value, size);
505e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
506e46d4714SMartin Matuska 		size = getxattr(accpath, name, value, size, 0, XATTR_NOFOLLOW);
507e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
5086c95142eSMartin Matuska 		size = lgetea(accpath, name, value, size);
509e46d4714SMartin Matuska #endif
510e46d4714SMartin Matuska 	} else {
511e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
512e46d4714SMartin Matuska 		size = getxattr(accpath, name, value, size);
513e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
514e46d4714SMartin Matuska 		size = getxattr(accpath, name, value, size, 0, 0);
515e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
5166c95142eSMartin Matuska 		size = getea(accpath, name, value, size);
5176c95142eSMartin Matuska #endif
518e46d4714SMartin Matuska 	}
519caf54c4fSMartin Matuska 
520caf54c4fSMartin Matuska 	if (size == -1) {
521caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno,
522caf54c4fSMartin Matuska 		    "Couldn't read extended attribute");
523caf54c4fSMartin Matuska 		return (ARCHIVE_WARN);
524caf54c4fSMartin Matuska 	}
525caf54c4fSMartin Matuska 
526caf54c4fSMartin Matuska 	archive_entry_xattr_add_entry(entry, name, value, size);
527caf54c4fSMartin Matuska 
528caf54c4fSMartin Matuska 	free(value);
529caf54c4fSMartin Matuska 	return (ARCHIVE_OK);
530caf54c4fSMartin Matuska }
531caf54c4fSMartin Matuska 
532caf54c4fSMartin Matuska static int
setup_xattrs(struct archive_read_disk * a,struct archive_entry * entry,int * fd)533caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a,
534fd082e96SMartin Matuska     struct archive_entry *entry, int *fd)
535caf54c4fSMartin Matuska {
536caf54c4fSMartin Matuska 	char *list, *p;
537caf54c4fSMartin Matuska 	const char *path;
538caf54c4fSMartin Matuska 	ssize_t list_size;
539caf54c4fSMartin Matuska 
54064287048SMartin Matuska 	path = NULL;
541caf54c4fSMartin Matuska 
542fd082e96SMartin Matuska 	if (*fd < 0) {
5434657548dSMartin Matuska 		path = archive_read_disk_entry_setup_path(a, entry, fd);
5444657548dSMartin Matuska 		if (path == NULL)
54564287048SMartin Matuska 			return (ARCHIVE_WARN);
546fd082e96SMartin Matuska 	}
547fd082e96SMartin Matuska 
548e46d4714SMartin Matuska 	if (*fd >= 0) {
549e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
550fd082e96SMartin Matuska 		list_size = flistxattr(*fd, NULL, 0);
551e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
552e46d4714SMartin Matuska 		list_size = flistxattr(*fd, NULL, 0, 0);
553e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
554fd082e96SMartin Matuska 		list_size = flistea(*fd, NULL, 0);
555e46d4714SMartin Matuska #endif
556e46d4714SMartin Matuska 	} else if (!a->follow_symlinks) {
557e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
558e46d4714SMartin Matuska 		list_size = llistxattr(path, NULL, 0);
559e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
560e46d4714SMartin Matuska 		list_size = listxattr(path, NULL, 0, XATTR_NOFOLLOW);
561e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
5626c95142eSMartin Matuska 		list_size = llistea(path, NULL, 0);
563e46d4714SMartin Matuska #endif
564e46d4714SMartin Matuska 	} else {
565e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
566e46d4714SMartin Matuska 		list_size = listxattr(path, NULL, 0);
567e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
568e46d4714SMartin Matuska 		list_size = listxattr(path, NULL, 0, 0);
569e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
5706c95142eSMartin Matuska 		list_size = listea(path, NULL, 0);
5716c95142eSMartin Matuska #endif
572e46d4714SMartin Matuska 	}
573caf54c4fSMartin Matuska 
574caf54c4fSMartin Matuska 	if (list_size == -1) {
5756c95142eSMartin Matuska 		if (errno == ENOTSUP || errno == ENOSYS)
576caf54c4fSMartin Matuska 			return (ARCHIVE_OK);
577caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno,
578caf54c4fSMartin Matuska 			"Couldn't list extended attributes");
579caf54c4fSMartin Matuska 		return (ARCHIVE_WARN);
580caf54c4fSMartin Matuska 	}
581caf54c4fSMartin Matuska 
582caf54c4fSMartin Matuska 	if (list_size == 0)
583caf54c4fSMartin Matuska 		return (ARCHIVE_OK);
584caf54c4fSMartin Matuska 
585caf54c4fSMartin Matuska 	if ((list = malloc(list_size)) == NULL) {
586caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno, "Out of memory");
587caf54c4fSMartin Matuska 		return (ARCHIVE_FATAL);
588caf54c4fSMartin Matuska 	}
589caf54c4fSMartin Matuska 
590e46d4714SMartin Matuska 	if (*fd >= 0) {
591e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
592fd082e96SMartin Matuska 		list_size = flistxattr(*fd, list, list_size);
593e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
594e46d4714SMartin Matuska 		list_size = flistxattr(*fd, list, list_size, 0);
595e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
596fd082e96SMartin Matuska 		list_size = flistea(*fd, list, list_size);
597e46d4714SMartin Matuska #endif
598e46d4714SMartin Matuska 	} else if (!a->follow_symlinks) {
599e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
600e46d4714SMartin Matuska 		list_size = llistxattr(path, list, list_size);
601e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
602e46d4714SMartin Matuska 		list_size = listxattr(path, list, list_size, XATTR_NOFOLLOW);
603e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
6046c95142eSMartin Matuska 		list_size = llistea(path, list, list_size);
605e46d4714SMartin Matuska #endif
606e46d4714SMartin Matuska 	} else {
607e46d4714SMartin Matuska #if ARCHIVE_XATTR_LINUX
608e46d4714SMartin Matuska 		list_size = listxattr(path, list, list_size);
609e46d4714SMartin Matuska #elif ARCHIVE_XATTR_DARWIN
610e46d4714SMartin Matuska 		list_size = listxattr(path, list, list_size, 0);
611e46d4714SMartin Matuska #elif ARCHIVE_XATTR_AIX
6126c95142eSMartin Matuska 		list_size = listea(path, list, list_size);
6136c95142eSMartin Matuska #endif
614e46d4714SMartin Matuska 	}
615caf54c4fSMartin Matuska 
616caf54c4fSMartin Matuska 	if (list_size == -1) {
617caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno,
618caf54c4fSMartin Matuska 			"Couldn't retrieve extended attributes");
619caf54c4fSMartin Matuska 		free(list);
620caf54c4fSMartin Matuska 		return (ARCHIVE_WARN);
621caf54c4fSMartin Matuska 	}
622caf54c4fSMartin Matuska 
623caf54c4fSMartin Matuska 	for (p = list; (p - list) < list_size; p += strlen(p) + 1) {
62443f9e382SMartin Matuska #if ARCHIVE_XATTR_LINUX
62543f9e382SMartin Matuska 		/* Linux: skip POSIX.1e ACL extended attributes */
62643f9e382SMartin Matuska 		if (strncmp(p, "system.", 7) == 0 &&
62743f9e382SMartin Matuska 		   (strcmp(p + 7, "posix_acl_access") == 0 ||
62843f9e382SMartin Matuska 		    strcmp(p + 7, "posix_acl_default") == 0))
629caf54c4fSMartin Matuska 			continue;
63043f9e382SMartin Matuska 		if (strncmp(p, "trusted.SGI_", 12) == 0 &&
63143f9e382SMartin Matuska 		   (strcmp(p + 12, "ACL_DEFAULT") == 0 ||
63243f9e382SMartin Matuska 		    strcmp(p + 12, "ACL_FILE") == 0))
63343f9e382SMartin Matuska 			continue;
63443f9e382SMartin Matuska 
63543f9e382SMartin Matuska 		/* Linux: xfsroot namespace is obsolete and unsupported */
63643f9e382SMartin Matuska 		if (strncmp(p, "xfsroot.", 8) == 0)
63743f9e382SMartin Matuska 			continue;
63843f9e382SMartin Matuska #endif
63964287048SMartin Matuska 		setup_xattr(a, entry, p, *fd, path);
640caf54c4fSMartin Matuska 	}
641caf54c4fSMartin Matuska 
642caf54c4fSMartin Matuska 	free(list);
643caf54c4fSMartin Matuska 	return (ARCHIVE_OK);
644caf54c4fSMartin Matuska }
645caf54c4fSMartin Matuska 
646e46d4714SMartin Matuska #elif ARCHIVE_XATTR_FREEBSD
647caf54c4fSMartin Matuska 
648caf54c4fSMartin Matuska /*
649caf54c4fSMartin Matuska  * FreeBSD extattr interface.
650caf54c4fSMartin Matuska  */
651caf54c4fSMartin Matuska 
652caf54c4fSMartin Matuska /* TODO: Implement this.  Follow the Linux model above, but
653caf54c4fSMartin Matuska  * with FreeBSD-specific system calls, of course.  Be careful
654caf54c4fSMartin Matuska  * to not include the system extattrs that hold ACLs; we handle
655caf54c4fSMartin Matuska  * those separately.
656caf54c4fSMartin Matuska  */
657caf54c4fSMartin Matuska static int
658caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
65964287048SMartin Matuska     int namespace, const char *name, const char *fullname, int fd,
66064287048SMartin Matuska     const char *path);
661caf54c4fSMartin Matuska 
662caf54c4fSMartin Matuska static int
setup_xattr(struct archive_read_disk * a,struct archive_entry * entry,int namespace,const char * name,const char * fullname,int fd,const char * accpath)663caf54c4fSMartin Matuska setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
66464287048SMartin Matuska     int namespace, const char *name, const char *fullname, int fd,
66564287048SMartin Matuska     const char *accpath)
666caf54c4fSMartin Matuska {
667caf54c4fSMartin Matuska 	ssize_t size;
668caf54c4fSMartin Matuska 	void *value = NULL;
669caf54c4fSMartin Matuska 
6706c95142eSMartin Matuska 	if (fd >= 0)
6716c95142eSMartin Matuska 		size = extattr_get_fd(fd, namespace, name, NULL, 0);
6726c95142eSMartin Matuska 	else if (!a->follow_symlinks)
673caf54c4fSMartin Matuska 		size = extattr_get_link(accpath, namespace, name, NULL, 0);
674caf54c4fSMartin Matuska 	else
675caf54c4fSMartin Matuska 		size = extattr_get_file(accpath, namespace, name, NULL, 0);
676caf54c4fSMartin Matuska 
677caf54c4fSMartin Matuska 	if (size == -1) {
678caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno,
679caf54c4fSMartin Matuska 		    "Couldn't query extended attribute");
680caf54c4fSMartin Matuska 		return (ARCHIVE_WARN);
681caf54c4fSMartin Matuska 	}
682caf54c4fSMartin Matuska 
683caf54c4fSMartin Matuska 	if (size > 0 && (value = malloc(size)) == NULL) {
684caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno, "Out of memory");
685caf54c4fSMartin Matuska 		return (ARCHIVE_FATAL);
686caf54c4fSMartin Matuska 	}
687caf54c4fSMartin Matuska 
6886c95142eSMartin Matuska 	if (fd >= 0)
6896c95142eSMartin Matuska 		size = extattr_get_fd(fd, namespace, name, value, size);
6906c95142eSMartin Matuska 	else if (!a->follow_symlinks)
691caf54c4fSMartin Matuska 		size = extattr_get_link(accpath, namespace, name, value, size);
692caf54c4fSMartin Matuska 	else
693caf54c4fSMartin Matuska 		size = extattr_get_file(accpath, namespace, name, value, size);
694caf54c4fSMartin Matuska 
695caf54c4fSMartin Matuska 	if (size == -1) {
696fd082e96SMartin Matuska 		free(value);
697caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno,
698caf54c4fSMartin Matuska 		    "Couldn't read extended attribute");
699caf54c4fSMartin Matuska 		return (ARCHIVE_WARN);
700caf54c4fSMartin Matuska 	}
701caf54c4fSMartin Matuska 
702caf54c4fSMartin Matuska 	archive_entry_xattr_add_entry(entry, fullname, value, size);
703caf54c4fSMartin Matuska 
704caf54c4fSMartin Matuska 	free(value);
705caf54c4fSMartin Matuska 	return (ARCHIVE_OK);
706caf54c4fSMartin Matuska }
707caf54c4fSMartin Matuska 
708caf54c4fSMartin Matuska static int
setup_xattrs_namespace(struct archive_read_disk * a,struct archive_entry * entry,int * fd,int namespace)709c3afd20fSMartin Matuska setup_xattrs_namespace(struct archive_read_disk *a,
710c3afd20fSMartin Matuska     struct archive_entry *entry, int *fd, int namespace)
711caf54c4fSMartin Matuska {
712caf54c4fSMartin Matuska 	char buff[512];
713caf54c4fSMartin Matuska 	char *list, *p;
714caf54c4fSMartin Matuska 	ssize_t list_size;
715caf54c4fSMartin Matuska 	const char *path;
716caf54c4fSMartin Matuska 
71764287048SMartin Matuska 	path = NULL;
718caf54c4fSMartin Matuska 
719fd082e96SMartin Matuska 	if (*fd < 0) {
7204657548dSMartin Matuska 		path = archive_read_disk_entry_setup_path(a, entry, fd);
7214657548dSMartin Matuska 		if (path == NULL)
72264287048SMartin Matuska 			return (ARCHIVE_WARN);
723fd082e96SMartin Matuska 	}
724fd082e96SMartin Matuska 
725fd082e96SMartin Matuska 	if (*fd >= 0)
726fd082e96SMartin Matuska 		list_size = extattr_list_fd(*fd, namespace, NULL, 0);
7276c95142eSMartin Matuska 	else if (!a->follow_symlinks)
728caf54c4fSMartin Matuska 		list_size = extattr_list_link(path, namespace, NULL, 0);
729caf54c4fSMartin Matuska 	else
730caf54c4fSMartin Matuska 		list_size = extattr_list_file(path, namespace, NULL, 0);
731caf54c4fSMartin Matuska 
732caf54c4fSMartin Matuska 	if (list_size == -1 && errno == EOPNOTSUPP)
733caf54c4fSMartin Matuska 		return (ARCHIVE_OK);
734c3afd20fSMartin Matuska 	if (list_size == -1 && errno == EPERM)
735c3afd20fSMartin Matuska 		return (ARCHIVE_OK);
736caf54c4fSMartin Matuska 	if (list_size == -1) {
737caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno,
738caf54c4fSMartin Matuska 			"Couldn't list extended attributes");
739caf54c4fSMartin Matuska 		return (ARCHIVE_WARN);
740caf54c4fSMartin Matuska 	}
741caf54c4fSMartin Matuska 
742caf54c4fSMartin Matuska 	if (list_size == 0)
743caf54c4fSMartin Matuska 		return (ARCHIVE_OK);
744caf54c4fSMartin Matuska 
745caf54c4fSMartin Matuska 	if ((list = malloc(list_size)) == NULL) {
746caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno, "Out of memory");
747caf54c4fSMartin Matuska 		return (ARCHIVE_FATAL);
748caf54c4fSMartin Matuska 	}
749caf54c4fSMartin Matuska 
750fd082e96SMartin Matuska 	if (*fd >= 0)
751fd082e96SMartin Matuska 		list_size = extattr_list_fd(*fd, namespace, list, list_size);
7526c95142eSMartin Matuska 	else if (!a->follow_symlinks)
753caf54c4fSMartin Matuska 		list_size = extattr_list_link(path, namespace, list, list_size);
754caf54c4fSMartin Matuska 	else
755caf54c4fSMartin Matuska 		list_size = extattr_list_file(path, namespace, list, list_size);
756caf54c4fSMartin Matuska 
757caf54c4fSMartin Matuska 	if (list_size == -1) {
758caf54c4fSMartin Matuska 		archive_set_error(&a->archive, errno,
759caf54c4fSMartin Matuska 			"Couldn't retrieve extended attributes");
760caf54c4fSMartin Matuska 		free(list);
761caf54c4fSMartin Matuska 		return (ARCHIVE_WARN);
762caf54c4fSMartin Matuska 	}
763caf54c4fSMartin Matuska 
764caf54c4fSMartin Matuska 	p = list;
765caf54c4fSMartin Matuska 	while ((p - list) < list_size) {
766caf54c4fSMartin Matuska 		size_t len = 255 & (int)*p;
767caf54c4fSMartin Matuska 		char *name;
768caf54c4fSMartin Matuska 
769c3afd20fSMartin Matuska 		if (namespace == EXTATTR_NAMESPACE_SYSTEM) {
770c3afd20fSMartin Matuska 			if (!strcmp(p + 1, "nfs4.acl") ||
771c3afd20fSMartin Matuska 			    !strcmp(p + 1, "posix1e.acl_access") ||
772c3afd20fSMartin Matuska 			    !strcmp(p + 1, "posix1e.acl_default")) {
773c3afd20fSMartin Matuska 				p += 1 + len;
774c3afd20fSMartin Matuska 				continue;
775c3afd20fSMartin Matuska 			}
776c3afd20fSMartin Matuska 			strcpy(buff, "system.");
777c3afd20fSMartin Matuska 		} else {
778caf54c4fSMartin Matuska 			strcpy(buff, "user.");
779c3afd20fSMartin Matuska 		}
780caf54c4fSMartin Matuska 		name = buff + strlen(buff);
781caf54c4fSMartin Matuska 		memcpy(name, p + 1, len);
782caf54c4fSMartin Matuska 		name[len] = '\0';
78364287048SMartin Matuska 		setup_xattr(a, entry, namespace, name, buff, *fd, path);
784caf54c4fSMartin Matuska 		p += 1 + len;
785caf54c4fSMartin Matuska 	}
786caf54c4fSMartin Matuska 
787caf54c4fSMartin Matuska 	free(list);
788caf54c4fSMartin Matuska 	return (ARCHIVE_OK);
789caf54c4fSMartin Matuska }
790caf54c4fSMartin Matuska 
791c3afd20fSMartin Matuska static int
setup_xattrs(struct archive_read_disk * a,struct archive_entry * entry,int * fd)792c3afd20fSMartin Matuska setup_xattrs(struct archive_read_disk *a,
793c3afd20fSMartin Matuska     struct archive_entry *entry, int *fd)
794c3afd20fSMartin Matuska {
795c3afd20fSMartin Matuska 	int namespaces[2];
796c3afd20fSMartin Matuska 	int i, res;
797c3afd20fSMartin Matuska 
798c3afd20fSMartin Matuska 	namespaces[0] = EXTATTR_NAMESPACE_USER;
799c3afd20fSMartin Matuska 	namespaces[1] = EXTATTR_NAMESPACE_SYSTEM;
800c3afd20fSMartin Matuska 
801c3afd20fSMartin Matuska 	for (i = 0; i < 2; i++) {
802c3afd20fSMartin Matuska 		res = setup_xattrs_namespace(a, entry, fd,
803c3afd20fSMartin Matuska 		    namespaces[i]);
804c3afd20fSMartin Matuska 		switch (res) {
805c3afd20fSMartin Matuska 			case (ARCHIVE_OK):
806c3afd20fSMartin Matuska 			case (ARCHIVE_WARN):
807c3afd20fSMartin Matuska 				break;
808c3afd20fSMartin Matuska 			default:
809c3afd20fSMartin Matuska 				return (res);
810c3afd20fSMartin Matuska 		}
811c3afd20fSMartin Matuska 	}
812c3afd20fSMartin Matuska 
813c3afd20fSMartin Matuska 	return (ARCHIVE_OK);
814c3afd20fSMartin Matuska }
815c3afd20fSMartin Matuska 
816caf54c4fSMartin Matuska #else
817caf54c4fSMartin Matuska 
818caf54c4fSMartin Matuska /*
819caf54c4fSMartin Matuska  * Generic (stub) extended attribute support.
820caf54c4fSMartin Matuska  */
821caf54c4fSMartin Matuska static int
setup_xattrs(struct archive_read_disk * a,struct archive_entry * entry,int * fd)822caf54c4fSMartin Matuska setup_xattrs(struct archive_read_disk *a,
823fd082e96SMartin Matuska     struct archive_entry *entry, int *fd)
824caf54c4fSMartin Matuska {
825caf54c4fSMartin Matuska 	(void)a;     /* UNUSED */
826caf54c4fSMartin Matuska 	(void)entry; /* UNUSED */
827caf54c4fSMartin Matuska 	(void)fd;    /* UNUSED */
828caf54c4fSMartin Matuska 	return (ARCHIVE_OK);
829caf54c4fSMartin Matuska }
830caf54c4fSMartin Matuska 
831caf54c4fSMartin Matuska #endif
8326c95142eSMartin Matuska 
8336c95142eSMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H)
8346c95142eSMartin Matuska 
8356c95142eSMartin Matuska /*
836d5d08d29SMartin Matuska  * Linux FIEMAP sparse interface.
8376c95142eSMartin Matuska  *
8386c95142eSMartin Matuska  * The FIEMAP ioctl returns an "extent" for each physical allocation
8396c95142eSMartin Matuska  * on disk.  We need to process those to generate a more compact list
8406c95142eSMartin Matuska  * of logical file blocks.  We also need to be very careful to use
8416c95142eSMartin Matuska  * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes
8426c95142eSMartin Matuska  * does not report allocations for newly-written data that hasn't
8436c95142eSMartin Matuska  * been synced to disk.
8446c95142eSMartin Matuska  *
8456c95142eSMartin Matuska  * It's important to return a minimal sparse file list because we want
8466c95142eSMartin Matuska  * to not trigger sparse file extensions if we don't have to, since
8476c95142eSMartin Matuska  * not all readers support them.
8486c95142eSMartin Matuska  */
8496c95142eSMartin Matuska 
8506c95142eSMartin Matuska static int
setup_sparse_fiemap(struct archive_read_disk * a,struct archive_entry * entry,int * fd)851d5d08d29SMartin Matuska setup_sparse_fiemap(struct archive_read_disk *a,
852fd082e96SMartin Matuska     struct archive_entry *entry, int *fd)
8536c95142eSMartin Matuska {
8546c95142eSMartin Matuska 	char buff[4096];
8556c95142eSMartin Matuska 	struct fiemap *fm;
8566c95142eSMartin Matuska 	struct fiemap_extent *fe;
8576c95142eSMartin Matuska 	int64_t size;
858cdf63a70SMartin Matuska 	int count, do_fiemap, iters;
8596c95142eSMartin Matuska 	int exit_sts = ARCHIVE_OK;
8604657548dSMartin Matuska 	const char *path;
8616c95142eSMartin Matuska 
8626c95142eSMartin Matuska 	if (archive_entry_filetype(entry) != AE_IFREG
8636c95142eSMartin Matuska 	    || archive_entry_size(entry) <= 0
8646c95142eSMartin Matuska 	    || archive_entry_hardlink(entry) != NULL)
8656c95142eSMartin Matuska 		return (ARCHIVE_OK);
8666c95142eSMartin Matuska 
867fd082e96SMartin Matuska 	if (*fd < 0) {
8684657548dSMartin Matuska 		path = archive_read_disk_entry_setup_path(a, entry, NULL);
8696c95142eSMartin Matuska 		if (path == NULL)
8704657548dSMartin Matuska 			return (ARCHIVE_FAILED);
8714657548dSMartin Matuska 
872fd082e96SMartin Matuska 		if (a->tree != NULL)
873fd082e96SMartin Matuska 			*fd = a->open_on_current_dir(a->tree, path,
874acc60b03SMartin Matuska 				O_RDONLY | O_NONBLOCK | O_CLOEXEC);
875fd082e96SMartin Matuska 		else
876acc60b03SMartin Matuska 			*fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
877fd082e96SMartin Matuska 		if (*fd < 0) {
8786c95142eSMartin Matuska 			archive_set_error(&a->archive, errno,
8796c95142eSMartin Matuska 			    "Can't open `%s'", path);
8806c95142eSMartin Matuska 			return (ARCHIVE_FAILED);
8816c95142eSMartin Matuska 		}
882acc60b03SMartin Matuska 		__archive_ensure_cloexec_flag(*fd);
8836c95142eSMartin Matuska 	}
8846c95142eSMartin Matuska 
885acc60b03SMartin Matuska 	/* Initialize buffer to avoid the error valgrind complains about. */
886acc60b03SMartin Matuska 	memset(buff, 0, sizeof(buff));
8876c95142eSMartin Matuska 	count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe);
8886c95142eSMartin Matuska 	fm = (struct fiemap *)buff;
8896c95142eSMartin Matuska 	fm->fm_start = 0;
8906c95142eSMartin Matuska 	fm->fm_length = ~0ULL;;
8916c95142eSMartin Matuska 	fm->fm_flags = FIEMAP_FLAG_SYNC;
8926c95142eSMartin Matuska 	fm->fm_extent_count = count;
8936c95142eSMartin Matuska 	do_fiemap = 1;
8946c95142eSMartin Matuska 	size = archive_entry_size(entry);
895cdf63a70SMartin Matuska 	for (iters = 0; ; ++iters) {
8966c95142eSMartin Matuska 		int i, r;
8976c95142eSMartin Matuska 
898fd082e96SMartin Matuska 		r = ioctl(*fd, FS_IOC_FIEMAP, fm);
8996c95142eSMartin Matuska 		if (r < 0) {
900fd082e96SMartin Matuska 			/* When something error happens, it is better we
901fd082e96SMartin Matuska 			 * should return ARCHIVE_OK because an earlier
902a2e802b7SMartin Matuska 			 * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */
903d5d08d29SMartin Matuska 			goto exit_setup_sparse_fiemap;
9046c95142eSMartin Matuska 		}
905cdf63a70SMartin Matuska 		if (fm->fm_mapped_extents == 0) {
906cdf63a70SMartin Matuska 			if (iters == 0) {
907cdf63a70SMartin Matuska 				/* Fully sparse file; insert a zero-length "data" entry */
908cdf63a70SMartin Matuska 				archive_entry_sparse_add_entry(entry, 0, 0);
909cdf63a70SMartin Matuska 			}
9106c95142eSMartin Matuska 			break;
911cdf63a70SMartin Matuska 		}
9126c95142eSMartin Matuska 		fe = fm->fm_extents;
9136c95142eSMartin Matuska 		for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) {
9146c95142eSMartin Matuska 			if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
9156c95142eSMartin Matuska 				/* The fe_length of the last block does not
9166c95142eSMartin Matuska 				 * adjust itself to its size files. */
9176c95142eSMartin Matuska 				int64_t length = fe->fe_length;
9186c95142eSMartin Matuska 				if (fe->fe_logical + length > (uint64_t)size)
9196c95142eSMartin Matuska 					length -= fe->fe_logical + length - size;
9206c95142eSMartin Matuska 				if (fe->fe_logical == 0 && length == size) {
9216c95142eSMartin Matuska 					/* This is not sparse. */
9226c95142eSMartin Matuska 					do_fiemap = 0;
9236c95142eSMartin Matuska 					break;
9246c95142eSMartin Matuska 				}
9256c95142eSMartin Matuska 				if (length > 0)
9266c95142eSMartin Matuska 					archive_entry_sparse_add_entry(entry,
9276c95142eSMartin Matuska 					    fe->fe_logical, length);
9286c95142eSMartin Matuska 			}
9296c95142eSMartin Matuska 			if (fe->fe_flags & FIEMAP_EXTENT_LAST)
9306c95142eSMartin Matuska 				do_fiemap = 0;
9316c95142eSMartin Matuska 		}
9326c95142eSMartin Matuska 		if (do_fiemap) {
9336c95142eSMartin Matuska 			fe = fm->fm_extents + fm->fm_mapped_extents -1;
9346c95142eSMartin Matuska 			fm->fm_start = fe->fe_logical + fe->fe_length;
9356c95142eSMartin Matuska 		} else
9366c95142eSMartin Matuska 			break;
9376c95142eSMartin Matuska 	}
938d5d08d29SMartin Matuska exit_setup_sparse_fiemap:
9396c95142eSMartin Matuska 	return (exit_sts);
9406c95142eSMartin Matuska }
9416c95142eSMartin Matuska 
942d5d08d29SMartin Matuska #if !defined(SEEK_HOLE) || !defined(SEEK_DATA)
943d5d08d29SMartin Matuska static int
setup_sparse(struct archive_read_disk * a,struct archive_entry * entry,int * fd)944d5d08d29SMartin Matuska setup_sparse(struct archive_read_disk *a,
945d5d08d29SMartin Matuska     struct archive_entry *entry, int *fd)
946d5d08d29SMartin Matuska {
947d5d08d29SMartin Matuska 	return setup_sparse_fiemap(a, entry, fd);
948d5d08d29SMartin Matuska }
949d5d08d29SMartin Matuska #endif
950d5d08d29SMartin Matuska #endif	/* defined(HAVE_LINUX_FIEMAP_H) */
951d5d08d29SMartin Matuska 
952d5d08d29SMartin Matuska #if defined(SEEK_HOLE) && defined(SEEK_DATA)
9536c95142eSMartin Matuska 
9546c95142eSMartin Matuska /*
955d5d08d29SMartin Matuska  * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris)
9566c95142eSMartin Matuska  */
9576c95142eSMartin Matuska 
9586c95142eSMartin Matuska static int
setup_sparse(struct archive_read_disk * a,struct archive_entry * entry,int * fd)9596c95142eSMartin Matuska setup_sparse(struct archive_read_disk *a,
960fd082e96SMartin Matuska     struct archive_entry *entry, int *fd)
9616c95142eSMartin Matuska {
9626c95142eSMartin Matuska 	int64_t size;
963d5d08d29SMartin Matuska 	off_t initial_off;
964d5d08d29SMartin Matuska 	off_t off_s, off_e;
9656c95142eSMartin Matuska 	int exit_sts = ARCHIVE_OK;
966cdf63a70SMartin Matuska 	int check_fully_sparse = 0;
9674657548dSMartin Matuska 	const char *path;
9686c95142eSMartin Matuska 
9696c95142eSMartin Matuska 	if (archive_entry_filetype(entry) != AE_IFREG
9706c95142eSMartin Matuska 	    || archive_entry_size(entry) <= 0
9716c95142eSMartin Matuska 	    || archive_entry_hardlink(entry) != NULL)
9726c95142eSMartin Matuska 		return (ARCHIVE_OK);
9736c95142eSMartin Matuska 
9746c95142eSMartin Matuska 	/* Does filesystem support the reporting of hole ? */
975a8fc61d5SMartin Matuska 	if (*fd < 0)
9764657548dSMartin Matuska 		path = archive_read_disk_entry_setup_path(a, entry, fd);
977a8fc61d5SMartin Matuska 	else
978a8fc61d5SMartin Matuska 		path = NULL;
979fd082e96SMartin Matuska 
980fd082e96SMartin Matuska 	if (*fd >= 0) {
981d5d08d29SMartin Matuska #ifdef _PC_MIN_HOLE_SIZE
982fd082e96SMartin Matuska 		if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0)
9836c95142eSMartin Matuska 			return (ARCHIVE_OK);
984d5d08d29SMartin Matuska #endif
985fd082e96SMartin Matuska 		initial_off = lseek(*fd, 0, SEEK_CUR);
9866c95142eSMartin Matuska 		if (initial_off != 0)
987fd082e96SMartin Matuska 			lseek(*fd, 0, SEEK_SET);
9886c95142eSMartin Matuska 	} else {
989a8fc61d5SMartin Matuska 		if (path == NULL)
990a8fc61d5SMartin Matuska 			return (ARCHIVE_FAILED);
991d5d08d29SMartin Matuska #ifdef _PC_MIN_HOLE_SIZE
9926c95142eSMartin Matuska 		if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
9936c95142eSMartin Matuska 			return (ARCHIVE_OK);
994d5d08d29SMartin Matuska #endif
995acc60b03SMartin Matuska 		*fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
996fd082e96SMartin Matuska 		if (*fd < 0) {
9976c95142eSMartin Matuska 			archive_set_error(&a->archive, errno,
9986c95142eSMartin Matuska 			    "Can't open `%s'", path);
9996c95142eSMartin Matuska 			return (ARCHIVE_FAILED);
10006c95142eSMartin Matuska 		}
1001acc60b03SMartin Matuska 		__archive_ensure_cloexec_flag(*fd);
10026c95142eSMartin Matuska 		initial_off = 0;
10036c95142eSMartin Matuska 	}
10046c95142eSMartin Matuska 
1005d5d08d29SMartin Matuska #ifndef _PC_MIN_HOLE_SIZE
1006d5d08d29SMartin Matuska 	/* Check if the underlying filesystem supports seek hole */
1007d5d08d29SMartin Matuska 	off_s = lseek(*fd, 0, SEEK_HOLE);
1008d5d08d29SMartin Matuska 	if (off_s < 0)
1009d5d08d29SMartin Matuska #if defined(HAVE_LINUX_FIEMAP_H)
1010d5d08d29SMartin Matuska 		return setup_sparse_fiemap(a, entry, fd);
1011d5d08d29SMartin Matuska #else
1012d5d08d29SMartin Matuska 		goto exit_setup_sparse;
1013d5d08d29SMartin Matuska #endif
1014d5d08d29SMartin Matuska 	else if (off_s > 0)
1015d5d08d29SMartin Matuska 		lseek(*fd, 0, SEEK_SET);
1016d5d08d29SMartin Matuska #endif
1017d5d08d29SMartin Matuska 
10186c95142eSMartin Matuska 	off_s = 0;
10196c95142eSMartin Matuska 	size = archive_entry_size(entry);
10206c95142eSMartin Matuska 	while (off_s < size) {
1021fd082e96SMartin Matuska 		off_s = lseek(*fd, off_s, SEEK_DATA);
10226c95142eSMartin Matuska 		if (off_s == (off_t)-1) {
1023cdf63a70SMartin Matuska 			if (errno == ENXIO) {
1024cdf63a70SMartin Matuska 				/* no more hole */
1025cdf63a70SMartin Matuska 				if (archive_entry_sparse_count(entry) == 0) {
1026cdf63a70SMartin Matuska 					/* Potentially a fully-sparse file. */
1027cdf63a70SMartin Matuska 					check_fully_sparse = 1;
1028cdf63a70SMartin Matuska 				}
1029cdf63a70SMartin Matuska 				break;
1030cdf63a70SMartin Matuska 			}
10316c95142eSMartin Matuska 			archive_set_error(&a->archive, errno,
10326c95142eSMartin Matuska 			    "lseek(SEEK_HOLE) failed");
10336c95142eSMartin Matuska 			exit_sts = ARCHIVE_FAILED;
10346c95142eSMartin Matuska 			goto exit_setup_sparse;
10356c95142eSMartin Matuska 		}
1036fd082e96SMartin Matuska 		off_e = lseek(*fd, off_s, SEEK_HOLE);
1037fd082e96SMartin Matuska 		if (off_e == (off_t)-1) {
10386c95142eSMartin Matuska 			if (errno == ENXIO) {
1039fd082e96SMartin Matuska 				off_e = lseek(*fd, 0, SEEK_END);
10406c95142eSMartin Matuska 				if (off_e != (off_t)-1)
10416c95142eSMartin Matuska 					break;/* no more data */
10426c95142eSMartin Matuska 			}
10436c95142eSMartin Matuska 			archive_set_error(&a->archive, errno,
10446c95142eSMartin Matuska 			    "lseek(SEEK_DATA) failed");
10456c95142eSMartin Matuska 			exit_sts = ARCHIVE_FAILED;
10466c95142eSMartin Matuska 			goto exit_setup_sparse;
10476c95142eSMartin Matuska 		}
10486c95142eSMartin Matuska 		if (off_s == 0 && off_e == size)
1049a2e802b7SMartin Matuska 			break;/* This is not sparse. */
10506c95142eSMartin Matuska 		archive_entry_sparse_add_entry(entry, off_s,
10516c95142eSMartin Matuska 			off_e - off_s);
10526c95142eSMartin Matuska 		off_s = off_e;
10536c95142eSMartin Matuska 	}
1054cdf63a70SMartin Matuska 
1055cdf63a70SMartin Matuska 	if (check_fully_sparse) {
1056cdf63a70SMartin Matuska 		if (lseek(*fd, 0, SEEK_HOLE) == 0 &&
1057cdf63a70SMartin Matuska 			lseek(*fd, 0, SEEK_END) == size) {
1058cdf63a70SMartin Matuska 			/* Fully sparse file; insert a zero-length "data" entry */
1059cdf63a70SMartin Matuska 			archive_entry_sparse_add_entry(entry, 0, 0);
1060cdf63a70SMartin Matuska 		}
1061cdf63a70SMartin Matuska 	}
10626c95142eSMartin Matuska exit_setup_sparse:
1063fd082e96SMartin Matuska 	lseek(*fd, initial_off, SEEK_SET);
10646c95142eSMartin Matuska 	return (exit_sts);
10656c95142eSMartin Matuska }
10666c95142eSMartin Matuska 
1067d5d08d29SMartin Matuska #elif !defined(HAVE_LINUX_FIEMAP_H)
10686c95142eSMartin Matuska 
10696c95142eSMartin Matuska /*
10706c95142eSMartin Matuska  * Generic (stub) sparse support.
10716c95142eSMartin Matuska  */
10726c95142eSMartin Matuska static int
setup_sparse(struct archive_read_disk * a,struct archive_entry * entry,int * fd)10736c95142eSMartin Matuska setup_sparse(struct archive_read_disk *a,
1074fd082e96SMartin Matuska     struct archive_entry *entry, int *fd)
10756c95142eSMartin Matuska {
10766c95142eSMartin Matuska 	(void)a;     /* UNUSED */
10776c95142eSMartin Matuska 	(void)entry; /* UNUSED */
10786c95142eSMartin Matuska 	(void)fd;    /* UNUSED */
10796c95142eSMartin Matuska 	return (ARCHIVE_OK);
10806c95142eSMartin Matuska }
10816c95142eSMartin Matuska 
10826c95142eSMartin Matuska #endif
10836c95142eSMartin Matuska 
10846c95142eSMartin Matuska #endif /* !defined(_WIN32) || defined(__CYGWIN__) */
10856c95142eSMartin Matuska 
1086