1228753Smm/*-
2228753Smm * Copyright (c) 2003-2009 Tim Kientzle
3238856Smm * Copyright (c) 2010-2012 Michihiro NAKAJIMA
4313571Smm * Copyright (c) 2016 Martin Matuska
5228753Smm * All rights reserved.
6228753Smm *
7228753Smm * Redistribution and use in source and binary forms, with or without
8228753Smm * modification, are permitted provided that the following conditions
9228753Smm * are met:
10228753Smm * 1. Redistributions of source code must retain the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer.
12228753Smm * 2. Redistributions in binary form must reproduce the above copyright
13228753Smm *    notice, this list of conditions and the following disclaimer in the
14228753Smm *    documentation and/or other materials provided with the distribution.
15228753Smm *
16228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26228753Smm */
27228753Smm
28228753Smm#include "archive_platform.h"
29316338Smm__FBSDID("$FreeBSD");
30228753Smm
31232153Smm/* This is the tree-walking code for POSIX systems. */
32232153Smm#if !defined(_WIN32) || defined(__CYGWIN__)
33232153Smm
34228753Smm#ifdef HAVE_SYS_TYPES_H
35228753Smm#include <sys/types.h>
36228753Smm#endif
37228753Smm#ifdef HAVE_SYS_EXTATTR_H
38228753Smm#include <sys/extattr.h>
39228753Smm#endif
40232153Smm#ifdef HAVE_SYS_IOCTL_H
41232153Smm#include <sys/ioctl.h>
42232153Smm#endif
43228753Smm#ifdef HAVE_SYS_PARAM_H
44228753Smm#include <sys/param.h>
45228753Smm#endif
46228753Smm#ifdef HAVE_SYS_STAT_H
47228753Smm#include <sys/stat.h>
48228753Smm#endif
49248616Smm#if defined(HAVE_SYS_XATTR_H)
50228753Smm#include <sys/xattr.h>
51248616Smm#elif defined(HAVE_ATTR_XATTR_H)
52248616Smm#include <attr/xattr.h>
53228753Smm#endif
54232153Smm#ifdef HAVE_SYS_EA_H
55232153Smm#include <sys/ea.h>
56232153Smm#endif
57232153Smm#ifdef HAVE_COPYFILE_H
58232153Smm#include <copyfile.h>
59232153Smm#endif
60228753Smm#ifdef HAVE_ERRNO_H
61228753Smm#include <errno.h>
62228753Smm#endif
63232153Smm#ifdef HAVE_FCNTL_H
64232153Smm#include <fcntl.h>
65232153Smm#endif
66228753Smm#ifdef HAVE_LIMITS_H
67228753Smm#include <limits.h>
68228753Smm#endif
69238856Smm#ifdef HAVE_LINUX_TYPES_H
70238856Smm#include <linux/types.h>
71238856Smm#endif
72232153Smm#ifdef HAVE_LINUX_FIEMAP_H
73232153Smm#include <linux/fiemap.h>
74228753Smm#endif
75232153Smm#ifdef HAVE_LINUX_FS_H
76232153Smm#include <linux/fs.h>
77232153Smm#endif
78232153Smm/*
79232153Smm * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
80232153Smm * As the include guards don't agree, the order of include is important.
81232153Smm */
82232153Smm#ifdef HAVE_LINUX_EXT2_FS_H
83232153Smm#include <linux/ext2_fs.h>      /* for Linux file flags */
84232153Smm#endif
85232153Smm#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
86232153Smm#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
87232153Smm#endif
88232153Smm#ifdef HAVE_PATHS_H
89232153Smm#include <paths.h>
90232153Smm#endif
91232153Smm#ifdef HAVE_UNISTD_H
92232153Smm#include <unistd.h>
93232153Smm#endif
94228753Smm
95228753Smm#include "archive.h"
96228753Smm#include "archive_entry.h"
97228753Smm#include "archive_private.h"
98228753Smm#include "archive_read_disk_private.h"
99228753Smm
100248616Smm#ifndef O_CLOEXEC
101248616Smm#define O_CLOEXEC	0
102248616Smm#endif
103248616Smm
104232153Smmstatic int setup_mac_metadata(struct archive_read_disk *,
105238856Smm    struct archive_entry *, int *fd);
106368708Smm#ifdef ARCHIVE_XATTR_FREEBSD
107368708Smmstatic int setup_xattrs_namespace(struct archive_read_disk *,
108368708Smm    struct archive_entry *, int *, int);
109368708Smm#endif
110228753Smmstatic int setup_xattrs(struct archive_read_disk *,
111238856Smm    struct archive_entry *, int *fd);
112232153Smmstatic int setup_sparse(struct archive_read_disk *,
113238856Smm    struct archive_entry *, int *fd);
114311042Smm#if defined(HAVE_LINUX_FIEMAP_H)
115311042Smmstatic int setup_sparse_fiemap(struct archive_read_disk *,
116311042Smm    struct archive_entry *, int *fd);
117311042Smm#endif
118228753Smm
119316338Smm#if !ARCHIVE_ACL_SUPPORT
120228753Smmint
121316338Smmarchive_read_disk_entry_setup_acls(struct archive_read_disk *a,
122316338Smm    struct archive_entry *entry, int *fd)
123316338Smm{
124316338Smm	(void)a;      /* UNUSED */
125316338Smm	(void)entry;  /* UNUSED */
126316338Smm	(void)fd;     /* UNUSED */
127316338Smm	return (ARCHIVE_OK);
128316338Smm}
129316338Smm#endif
130316338Smm
131316338Smm/*
132316338Smm * Enter working directory and return working pathname of archive_entry.
133316338Smm * If a pointer to an integer is provided and its value is below zero
134324418Smm * open a file descriptor on this pathname.
135316338Smm */
136316338Smmconst char *
137316338Smmarchive_read_disk_entry_setup_path(struct archive_read_disk *a,
138316338Smm    struct archive_entry *entry, int *fd)
139316338Smm{
140316338Smm	const char *path;
141316338Smm
142316338Smm	path = archive_entry_sourcepath(entry);
143316338Smm
144316338Smm	if (path == NULL || (a->tree != NULL &&
145316338Smm	    a->tree_enter_working_dir(a->tree) != 0))
146316338Smm		path = archive_entry_pathname(entry);
147316338Smm	if (path == NULL) {
148316338Smm		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
149316338Smm		   "Couldn't determine path");
150316338Smm	} else if (fd != NULL && *fd < 0 && a->tree != NULL &&
151316338Smm	    (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)) {
152316338Smm		*fd = a->open_on_current_dir(a->tree, path,
153316338Smm		    O_RDONLY | O_NONBLOCK);
154316338Smm	}
155316338Smm	return (path);
156316338Smm}
157316338Smm
158316338Smmint
159228753Smmarchive_read_disk_entry_from_file(struct archive *_a,
160228753Smm    struct archive_entry *entry,
161232153Smm    int fd,
162232153Smm    const struct stat *st)
163228753Smm{
164228753Smm	struct archive_read_disk *a = (struct archive_read_disk *)_a;
165228753Smm	const char *path, *name;
166228753Smm	struct stat s;
167228753Smm	int initial_fd = fd;
168228753Smm	int r, r1;
169228753Smm
170346105Smm	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
171346105Smm		"archive_read_disk_entry_from_file");
172346105Smm
173228753Smm	archive_clear_error(_a);
174228753Smm	path = archive_entry_sourcepath(entry);
175228753Smm	if (path == NULL)
176228753Smm		path = archive_entry_pathname(entry);
177228753Smm
178232153Smm	if (a->tree == NULL) {
179232153Smm		if (st == NULL) {
180228753Smm#if HAVE_FSTAT
181232153Smm			if (fd >= 0) {
182232153Smm				if (fstat(fd, &s) != 0) {
183232153Smm					archive_set_error(&a->archive, errno,
184232153Smm					    "Can't fstat");
185232153Smm					return (ARCHIVE_FAILED);
186232153Smm				}
187232153Smm			} else
188228753Smm#endif
189228753Smm#if HAVE_LSTAT
190232153Smm			if (!a->follow_symlinks) {
191232153Smm				if (lstat(path, &s) != 0) {
192232153Smm					archive_set_error(&a->archive, errno,
193232153Smm					    "Can't lstat %s", path);
194232153Smm					return (ARCHIVE_FAILED);
195232153Smm				}
196232153Smm			} else
197232153Smm#endif
198348608Smm			if (la_stat(path, &s) != 0) {
199228753Smm				archive_set_error(&a->archive, errno,
200232153Smm				    "Can't stat %s", path);
201228753Smm				return (ARCHIVE_FAILED);
202228753Smm			}
203232153Smm			st = &s;
204228753Smm		}
205232153Smm		archive_entry_copy_stat(entry, st);
206228753Smm	}
207228753Smm
208238909Smm	/* Lookup uname/gname */
209238909Smm	name = archive_read_disk_uname(_a, archive_entry_uid(entry));
210238909Smm	if (name != NULL)
211238909Smm		archive_entry_copy_uname(entry, name);
212238909Smm	name = archive_read_disk_gname(_a, archive_entry_gid(entry));
213238909Smm	if (name != NULL)
214238909Smm		archive_entry_copy_gname(entry, name);
215238909Smm
216228753Smm#ifdef HAVE_STRUCT_STAT_ST_FLAGS
217228753Smm	/* On FreeBSD, we get flags for free with the stat. */
218228753Smm	/* TODO: Does this belong in copy_stat()? */
219315433Smm	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0)
220228753Smm		archive_entry_set_fflags(entry, st->st_flags, 0);
221228753Smm#endif
222228753Smm
223315433Smm#if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
224315433Smm    (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
225232153Smm	/* Linux requires an extra ioctl to pull the flags.  Although
226232153Smm	 * this is an extra step, it has a nice side-effect: We get an
227232153Smm	 * open file descriptor which we can use in the subsequent lookups. */
228315433Smm	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 &&
229315433Smm	    (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
230238856Smm		if (fd < 0) {
231238856Smm			if (a->tree != NULL)
232238856Smm				fd = a->open_on_current_dir(a->tree, path,
233248616Smm					O_RDONLY | O_NONBLOCK | O_CLOEXEC);
234238856Smm			else
235248616Smm				fd = open(path, O_RDONLY | O_NONBLOCK |
236248616Smm						O_CLOEXEC);
237248616Smm			__archive_ensure_cloexec_flag(fd);
238238856Smm		}
239232153Smm		if (fd >= 0) {
240248616Smm			int stflags;
241315433Smm			r = ioctl(fd,
242315433Smm#if defined(FS_IOC_GETFLAGS)
243315433Smm			    FS_IOC_GETFLAGS,
244315433Smm#else
245315433Smm			    EXT2_IOC_GETFLAGS,
246315433Smm#endif
247315433Smm			    &stflags);
248232153Smm			if (r == 0 && stflags != 0)
249232153Smm				archive_entry_set_fflags(entry, stflags, 0);
250232153Smm		}
251232153Smm	}
252232153Smm#endif
253232153Smm
254232153Smm#if defined(HAVE_READLINK) || defined(HAVE_READLINKAT)
255228753Smm	if (S_ISLNK(st->st_mode)) {
256358090Smm		size_t linkbuffer_len = st->st_size;
257232153Smm		char *linkbuffer;
258232153Smm		int lnklen;
259232153Smm
260358090Smm		linkbuffer = malloc(linkbuffer_len + 1);
261232153Smm		if (linkbuffer == NULL) {
262232153Smm			archive_set_error(&a->archive, ENOMEM,
263232153Smm			    "Couldn't read link data");
264232153Smm			return (ARCHIVE_FAILED);
265232153Smm		}
266238856Smm		if (a->tree != NULL) {
267232153Smm#ifdef HAVE_READLINKAT
268238856Smm			lnklen = readlinkat(a->tree_current_dir_fd(a->tree),
269238856Smm			    path, linkbuffer, linkbuffer_len);
270238856Smm#else
271238856Smm			if (a->tree_enter_working_dir(a->tree) != 0) {
272238856Smm				archive_set_error(&a->archive, errno,
273238856Smm				    "Couldn't read link data");
274238856Smm				free(linkbuffer);
275238856Smm				return (ARCHIVE_FAILED);
276238856Smm			}
277238856Smm			lnklen = readlink(path, linkbuffer, linkbuffer_len);
278232153Smm#endif /* HAVE_READLINKAT */
279238856Smm		} else
280238856Smm			lnklen = readlink(path, linkbuffer, linkbuffer_len);
281228753Smm		if (lnklen < 0) {
282228753Smm			archive_set_error(&a->archive, errno,
283228753Smm			    "Couldn't read link data");
284232153Smm			free(linkbuffer);
285228753Smm			return (ARCHIVE_FAILED);
286228753Smm		}
287358090Smm		linkbuffer[lnklen] = '\0';
288228753Smm		archive_entry_set_symlink(entry, linkbuffer);
289232153Smm		free(linkbuffer);
290228753Smm	}
291232153Smm#endif /* HAVE_READLINK || HAVE_READLINKAT */
292228753Smm
293315433Smm	r = 0;
294315433Smm	if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0)
295316338Smm		r = archive_read_disk_entry_setup_acls(a, entry, &fd);
296315433Smm	if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) {
297302001Smm		r1 = setup_xattrs(a, entry, &fd);
298302001Smm		if (r1 < r)
299302001Smm			r = r1;
300302001Smm	}
301315433Smm	if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {
302238856Smm		r1 = setup_mac_metadata(a, entry, &fd);
303238856Smm		if (r1 < r)
304238856Smm			r = r1;
305238856Smm	}
306238856Smm	r1 = setup_sparse(a, entry, &fd);
307232153Smm	if (r1 < r)
308232153Smm		r = r1;
309232153Smm
310228753Smm	/* If we opened the file earlier in this function, close it. */
311228753Smm	if (initial_fd != fd)
312228753Smm		close(fd);
313228753Smm	return (r);
314228753Smm}
315228753Smm
316232153Smm#if defined(__APPLE__) && defined(HAVE_COPYFILE_H)
317232153Smm/*
318232153Smm * The Mac OS "copyfile()" API copies the extended metadata for a
319232153Smm * file into a separate file in AppleDouble format (see RFC 1740).
320232153Smm *
321232153Smm * Mac OS tar and cpio implementations store this extended
322232153Smm * metadata as a separate entry just before the regular entry
323232153Smm * with a "._" prefix added to the filename.
324232153Smm *
325232153Smm * Note that this is currently done unconditionally; the tar program has
326232153Smm * an option to discard this information before the archive is written.
327232153Smm *
328232153Smm * TODO: If there's a failure, report it and return ARCHIVE_WARN.
329232153Smm */
330232153Smmstatic int
331232153Smmsetup_mac_metadata(struct archive_read_disk *a,
332238856Smm    struct archive_entry *entry, int *fd)
333232153Smm{
334232153Smm	int tempfd = -1;
335232153Smm	int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;
336232153Smm	struct stat copyfile_stat;
337232153Smm	int ret = ARCHIVE_OK;
338248616Smm	void *buff = NULL;
339232153Smm	int have_attrs;
340248616Smm	const char *name, *tempdir;
341248616Smm	struct archive_string tempfile;
342232153Smm
343238856Smm	(void)fd; /* UNUSED */
344316338Smm
345316338Smm	name = archive_read_disk_entry_setup_path(a, entry, NULL);
346232153Smm	if (name == NULL)
347232153Smm		return (ARCHIVE_WARN);
348232153Smm
349232153Smm	/* Short-circuit if there's nothing to do. */
350232153Smm	have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
351232153Smm	if (have_attrs == -1) {
352232153Smm		archive_set_error(&a->archive, errno,
353232153Smm			"Could not check extended attributes");
354232153Smm		return (ARCHIVE_WARN);
355232153Smm	}
356232153Smm	if (have_attrs == 0)
357232153Smm		return (ARCHIVE_OK);
358232153Smm
359232153Smm	tempdir = NULL;
360232153Smm	if (issetugid() == 0)
361232153Smm		tempdir = getenv("TMPDIR");
362232153Smm	if (tempdir == NULL)
363232153Smm		tempdir = _PATH_TMP;
364248616Smm	archive_string_init(&tempfile);
365248616Smm	archive_strcpy(&tempfile, tempdir);
366248616Smm	archive_strcat(&tempfile, "tar.md.XXXXXX");
367248616Smm	tempfd = mkstemp(tempfile.s);
368248616Smm	if (tempfd < 0) {
369248616Smm		archive_set_error(&a->archive, errno,
370248616Smm		    "Could not open extended attribute file");
371248616Smm		ret = ARCHIVE_WARN;
372248616Smm		goto cleanup;
373248616Smm	}
374248616Smm	__archive_ensure_cloexec_flag(tempfd);
375232153Smm
376232153Smm	/* XXX I wish copyfile() could pack directly to a memory
377232153Smm	 * buffer; that would avoid the temp file here.  For that
378232153Smm	 * matter, it would be nice if fcopyfile() actually worked,
379232153Smm	 * that would reduce the many open/close races here. */
380248616Smm	if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) {
381232153Smm		archive_set_error(&a->archive, errno,
382232153Smm		    "Could not pack extended attributes");
383232153Smm		ret = ARCHIVE_WARN;
384232153Smm		goto cleanup;
385232153Smm	}
386232153Smm	if (fstat(tempfd, &copyfile_stat)) {
387232153Smm		archive_set_error(&a->archive, errno,
388232153Smm		    "Could not check size of extended attributes");
389232153Smm		ret = ARCHIVE_WARN;
390232153Smm		goto cleanup;
391232153Smm	}
392232153Smm	buff = malloc(copyfile_stat.st_size);
393232153Smm	if (buff == NULL) {
394232153Smm		archive_set_error(&a->archive, errno,
395232153Smm		    "Could not allocate memory for extended attributes");
396232153Smm		ret = ARCHIVE_WARN;
397232153Smm		goto cleanup;
398232153Smm	}
399232153Smm	if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) {
400232153Smm		archive_set_error(&a->archive, errno,
401232153Smm		    "Could not read extended attributes into memory");
402232153Smm		ret = ARCHIVE_WARN;
403232153Smm		goto cleanup;
404232153Smm	}
405232153Smm	archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size);
406232153Smm
407232153Smmcleanup:
408248616Smm	if (tempfd >= 0) {
409232153Smm		close(tempfd);
410248616Smm		unlink(tempfile.s);
411248616Smm	}
412248616Smm	archive_string_free(&tempfile);
413248616Smm	free(buff);
414232153Smm	return (ret);
415232153Smm}
416232153Smm
417232153Smm#else
418232153Smm
419232153Smm/*
420232153Smm * Stub implementation for non-Mac systems.
421232153Smm */
422232153Smmstatic int
423232153Smmsetup_mac_metadata(struct archive_read_disk *a,
424238856Smm    struct archive_entry *entry, int *fd)
425232153Smm{
426232153Smm	(void)a; /* UNUSED */
427232153Smm	(void)entry; /* UNUSED */
428232153Smm	(void)fd; /* UNUSED */
429232153Smm	return (ARCHIVE_OK);
430232153Smm}
431232153Smm#endif
432232153Smm
433316338Smm#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
434232153Smm
435228753Smm/*
436316338Smm * Linux, Darwin and AIX extended attribute support.
437228753Smm *
438228753Smm * TODO:  By using a stack-allocated buffer for the first
439228753Smm * call to getxattr(), we might be able to avoid the second
440228753Smm * call entirely.  We only need the second call if the
441228753Smm * stack-allocated buffer is too small.  But a modest buffer
442228753Smm * of 1024 bytes or so will often be big enough.  Same applies
443228753Smm * to listxattr().
444228753Smm */
445228753Smm
446228753Smm
447228753Smmstatic int
448228753Smmsetup_xattr(struct archive_read_disk *a,
449315433Smm    struct archive_entry *entry, const char *name, int fd, const char *accpath)
450228753Smm{
451228753Smm	ssize_t size;
452228753Smm	void *value = NULL;
453228753Smm
454316338Smm
455316338Smm	if (fd >= 0) {
456316338Smm#if ARCHIVE_XATTR_LINUX
457232153Smm		size = fgetxattr(fd, name, NULL, 0);
458316338Smm#elif ARCHIVE_XATTR_DARWIN
459316338Smm		size = fgetxattr(fd, name, NULL, 0, 0, 0);
460316338Smm#elif ARCHIVE_XATTR_AIX
461316338Smm		size = fgetea(fd, name, NULL, 0);
462316338Smm#endif
463316338Smm	} else if (!a->follow_symlinks) {
464316338Smm#if ARCHIVE_XATTR_LINUX
465228753Smm		size = lgetxattr(accpath, name, NULL, 0);
466316338Smm#elif ARCHIVE_XATTR_DARWIN
467316338Smm		size = getxattr(accpath, name, NULL, 0, 0, XATTR_NOFOLLOW);
468316338Smm#elif ARCHIVE_XATTR_AIX
469316338Smm		size = lgetea(accpath, name, NULL, 0);
470316338Smm#endif
471316338Smm	} else {
472316338Smm#if ARCHIVE_XATTR_LINUX
473228753Smm		size = getxattr(accpath, name, NULL, 0);
474316338Smm#elif ARCHIVE_XATTR_DARWIN
475316338Smm		size = getxattr(accpath, name, NULL, 0, 0, 0);
476316338Smm#elif ARCHIVE_XATTR_AIX
477232153Smm		size = getea(accpath, name, NULL, 0);
478232153Smm#endif
479316338Smm	}
480228753Smm
481228753Smm	if (size == -1) {
482228753Smm		archive_set_error(&a->archive, errno,
483228753Smm		    "Couldn't query extended attribute");
484228753Smm		return (ARCHIVE_WARN);
485228753Smm	}
486228753Smm
487228753Smm	if (size > 0 && (value = malloc(size)) == NULL) {
488228753Smm		archive_set_error(&a->archive, errno, "Out of memory");
489228753Smm		return (ARCHIVE_FATAL);
490228753Smm	}
491228753Smm
492316338Smm
493316338Smm	if (fd >= 0) {
494316338Smm#if ARCHIVE_XATTR_LINUX
495232153Smm		size = fgetxattr(fd, name, value, size);
496316338Smm#elif ARCHIVE_XATTR_DARWIN
497316338Smm		size = fgetxattr(fd, name, value, size, 0, 0);
498316338Smm#elif ARCHIVE_XATTR_AIX
499316338Smm		size = fgetea(fd, name, value, size);
500316338Smm#endif
501316338Smm	} else if (!a->follow_symlinks) {
502316338Smm#if ARCHIVE_XATTR_LINUX
503228753Smm		size = lgetxattr(accpath, name, value, size);
504316338Smm#elif ARCHIVE_XATTR_DARWIN
505316338Smm		size = getxattr(accpath, name, value, size, 0, XATTR_NOFOLLOW);
506316338Smm#elif ARCHIVE_XATTR_AIX
507316338Smm		size = lgetea(accpath, name, value, size);
508316338Smm#endif
509316338Smm	} else {
510316338Smm#if ARCHIVE_XATTR_LINUX
511228753Smm		size = getxattr(accpath, name, value, size);
512316338Smm#elif ARCHIVE_XATTR_DARWIN
513316338Smm		size = getxattr(accpath, name, value, size, 0, 0);
514316338Smm#elif ARCHIVE_XATTR_AIX
515232153Smm		size = getea(accpath, name, value, size);
516232153Smm#endif
517316338Smm	}
518228753Smm
519228753Smm	if (size == -1) {
520228753Smm		archive_set_error(&a->archive, errno,
521228753Smm		    "Couldn't read extended attribute");
522228753Smm		return (ARCHIVE_WARN);
523228753Smm	}
524228753Smm
525228753Smm	archive_entry_xattr_add_entry(entry, name, value, size);
526228753Smm
527228753Smm	free(value);
528228753Smm	return (ARCHIVE_OK);
529228753Smm}
530228753Smm
531228753Smmstatic int
532228753Smmsetup_xattrs(struct archive_read_disk *a,
533238856Smm    struct archive_entry *entry, int *fd)
534228753Smm{
535228753Smm	char *list, *p;
536228753Smm	const char *path;
537228753Smm	ssize_t list_size;
538228753Smm
539315433Smm	path = NULL;
540228753Smm
541315433Smm	if (*fd < 0) {
542316338Smm		path = archive_read_disk_entry_setup_path(a, entry, fd);
543316338Smm		if (path == NULL)
544315433Smm			return (ARCHIVE_WARN);
545238856Smm	}
546238856Smm
547316338Smm	if (*fd >= 0) {
548316338Smm#if ARCHIVE_XATTR_LINUX
549238856Smm		list_size = flistxattr(*fd, NULL, 0);
550316338Smm#elif ARCHIVE_XATTR_DARWIN
551316338Smm		list_size = flistxattr(*fd, NULL, 0, 0);
552316338Smm#elif ARCHIVE_XATTR_AIX
553316338Smm		list_size = flistea(*fd, NULL, 0);
554316338Smm#endif
555316338Smm	} else if (!a->follow_symlinks) {
556316338Smm#if ARCHIVE_XATTR_LINUX
557228753Smm		list_size = llistxattr(path, NULL, 0);
558316338Smm#elif ARCHIVE_XATTR_DARWIN
559316338Smm		list_size = listxattr(path, NULL, 0, XATTR_NOFOLLOW);
560316338Smm#elif ARCHIVE_XATTR_AIX
561316338Smm		list_size = llistea(path, NULL, 0);
562316338Smm#endif
563316338Smm	} else {
564316338Smm#if ARCHIVE_XATTR_LINUX
565228753Smm		list_size = listxattr(path, NULL, 0);
566316338Smm#elif ARCHIVE_XATTR_DARWIN
567316338Smm		list_size = listxattr(path, NULL, 0, 0);
568316338Smm#elif ARCHIVE_XATTR_AIX
569232153Smm		list_size = listea(path, NULL, 0);
570232153Smm#endif
571316338Smm	}
572228753Smm
573228753Smm	if (list_size == -1) {
574232153Smm		if (errno == ENOTSUP || errno == ENOSYS)
575228753Smm			return (ARCHIVE_OK);
576228753Smm		archive_set_error(&a->archive, errno,
577228753Smm			"Couldn't list extended attributes");
578228753Smm		return (ARCHIVE_WARN);
579228753Smm	}
580228753Smm
581228753Smm	if (list_size == 0)
582228753Smm		return (ARCHIVE_OK);
583228753Smm
584228753Smm	if ((list = malloc(list_size)) == NULL) {
585228753Smm		archive_set_error(&a->archive, errno, "Out of memory");
586228753Smm		return (ARCHIVE_FATAL);
587228753Smm	}
588228753Smm
589316338Smm	if (*fd >= 0) {
590316338Smm#if ARCHIVE_XATTR_LINUX
591238856Smm		list_size = flistxattr(*fd, list, list_size);
592316338Smm#elif ARCHIVE_XATTR_DARWIN
593316338Smm		list_size = flistxattr(*fd, list, list_size, 0);
594316338Smm#elif ARCHIVE_XATTR_AIX
595316338Smm		list_size = flistea(*fd, list, list_size);
596316338Smm#endif
597316338Smm	} else if (!a->follow_symlinks) {
598316338Smm#if ARCHIVE_XATTR_LINUX
599228753Smm		list_size = llistxattr(path, list, list_size);
600316338Smm#elif ARCHIVE_XATTR_DARWIN
601316338Smm		list_size = listxattr(path, list, list_size, XATTR_NOFOLLOW);
602316338Smm#elif ARCHIVE_XATTR_AIX
603316338Smm		list_size = llistea(path, list, list_size);
604316338Smm#endif
605316338Smm	} else {
606316338Smm#if ARCHIVE_XATTR_LINUX
607228753Smm		list_size = listxattr(path, list, list_size);
608316338Smm#elif ARCHIVE_XATTR_DARWIN
609316338Smm		list_size = listxattr(path, list, list_size, 0);
610316338Smm#elif ARCHIVE_XATTR_AIX
611232153Smm		list_size = listea(path, list, list_size);
612232153Smm#endif
613316338Smm	}
614228753Smm
615228753Smm	if (list_size == -1) {
616228753Smm		archive_set_error(&a->archive, errno,
617228753Smm			"Couldn't retrieve extended attributes");
618228753Smm		free(list);
619228753Smm		return (ARCHIVE_WARN);
620228753Smm	}
621228753Smm
622228753Smm	for (p = list; (p - list) < list_size; p += strlen(p) + 1) {
623353377Smm#if ARCHIVE_XATTR_LINUX
624353377Smm		/* Linux: skip POSIX.1e ACL extended attributes */
625353377Smm		if (strncmp(p, "system.", 7) == 0 &&
626353377Smm		   (strcmp(p + 7, "posix_acl_access") == 0 ||
627353377Smm		    strcmp(p + 7, "posix_acl_default") == 0))
628228753Smm			continue;
629353377Smm		if (strncmp(p, "trusted.SGI_", 12) == 0 &&
630353377Smm		   (strcmp(p + 12, "ACL_DEFAULT") == 0 ||
631353377Smm		    strcmp(p + 12, "ACL_FILE") == 0))
632353377Smm			continue;
633353377Smm
634353377Smm		/* Linux: xfsroot namespace is obsolete and unsupported */
635353377Smm		if (strncmp(p, "xfsroot.", 8) == 0)
636353377Smm			continue;
637353377Smm#endif
638315433Smm		setup_xattr(a, entry, p, *fd, path);
639228753Smm	}
640228753Smm
641228753Smm	free(list);
642228753Smm	return (ARCHIVE_OK);
643228753Smm}
644228753Smm
645316338Smm#elif ARCHIVE_XATTR_FREEBSD
646228753Smm
647228753Smm/*
648228753Smm * FreeBSD extattr interface.
649228753Smm */
650228753Smm
651228753Smm/* TODO: Implement this.  Follow the Linux model above, but
652228753Smm * with FreeBSD-specific system calls, of course.  Be careful
653228753Smm * to not include the system extattrs that hold ACLs; we handle
654228753Smm * those separately.
655228753Smm */
656228753Smmstatic int
657228753Smmsetup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
658315433Smm    int namespace, const char *name, const char *fullname, int fd,
659315433Smm    const char *path);
660228753Smm
661228753Smmstatic int
662228753Smmsetup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
663315433Smm    int namespace, const char *name, const char *fullname, int fd,
664315433Smm    const char *accpath)
665228753Smm{
666228753Smm	ssize_t size;
667228753Smm	void *value = NULL;
668228753Smm
669232153Smm	if (fd >= 0)
670232153Smm		size = extattr_get_fd(fd, namespace, name, NULL, 0);
671232153Smm	else if (!a->follow_symlinks)
672228753Smm		size = extattr_get_link(accpath, namespace, name, NULL, 0);
673228753Smm	else
674228753Smm		size = extattr_get_file(accpath, namespace, name, NULL, 0);
675228753Smm
676228753Smm	if (size == -1) {
677228753Smm		archive_set_error(&a->archive, errno,
678228753Smm		    "Couldn't query extended attribute");
679228753Smm		return (ARCHIVE_WARN);
680228753Smm	}
681228753Smm
682228753Smm	if (size > 0 && (value = malloc(size)) == NULL) {
683228753Smm		archive_set_error(&a->archive, errno, "Out of memory");
684228753Smm		return (ARCHIVE_FATAL);
685228753Smm	}
686228753Smm
687232153Smm	if (fd >= 0)
688232153Smm		size = extattr_get_fd(fd, namespace, name, value, size);
689232153Smm	else if (!a->follow_symlinks)
690228753Smm		size = extattr_get_link(accpath, namespace, name, value, size);
691228753Smm	else
692228753Smm		size = extattr_get_file(accpath, namespace, name, value, size);
693228753Smm
694228753Smm	if (size == -1) {
695238856Smm		free(value);
696228753Smm		archive_set_error(&a->archive, errno,
697228753Smm		    "Couldn't read extended attribute");
698228753Smm		return (ARCHIVE_WARN);
699228753Smm	}
700228753Smm
701228753Smm	archive_entry_xattr_add_entry(entry, fullname, value, size);
702228753Smm
703228753Smm	free(value);
704228753Smm	return (ARCHIVE_OK);
705228753Smm}
706228753Smm
707228753Smmstatic int
708368708Smmsetup_xattrs_namespace(struct archive_read_disk *a,
709368708Smm    struct archive_entry *entry, int *fd, int namespace)
710228753Smm{
711228753Smm	char buff[512];
712228753Smm	char *list, *p;
713228753Smm	ssize_t list_size;
714228753Smm	const char *path;
715228753Smm
716315433Smm	path = NULL;
717228753Smm
718315433Smm	if (*fd < 0) {
719316338Smm		path = archive_read_disk_entry_setup_path(a, entry, fd);
720316338Smm		if (path == NULL)
721315433Smm			return (ARCHIVE_WARN);
722238856Smm	}
723238856Smm
724238856Smm	if (*fd >= 0)
725238856Smm		list_size = extattr_list_fd(*fd, namespace, NULL, 0);
726232153Smm	else if (!a->follow_symlinks)
727228753Smm		list_size = extattr_list_link(path, namespace, NULL, 0);
728228753Smm	else
729228753Smm		list_size = extattr_list_file(path, namespace, NULL, 0);
730228753Smm
731228753Smm	if (list_size == -1 && errno == EOPNOTSUPP)
732228753Smm		return (ARCHIVE_OK);
733368708Smm	if (list_size == -1 && errno == EPERM)
734368708Smm		return (ARCHIVE_OK);
735228753Smm	if (list_size == -1) {
736228753Smm		archive_set_error(&a->archive, errno,
737228753Smm			"Couldn't list extended attributes");
738228753Smm		return (ARCHIVE_WARN);
739228753Smm	}
740228753Smm
741228753Smm	if (list_size == 0)
742228753Smm		return (ARCHIVE_OK);
743228753Smm
744228753Smm	if ((list = malloc(list_size)) == NULL) {
745228753Smm		archive_set_error(&a->archive, errno, "Out of memory");
746228753Smm		return (ARCHIVE_FATAL);
747228753Smm	}
748228753Smm
749238856Smm	if (*fd >= 0)
750238856Smm		list_size = extattr_list_fd(*fd, namespace, list, list_size);
751232153Smm	else if (!a->follow_symlinks)
752228753Smm		list_size = extattr_list_link(path, namespace, list, list_size);
753228753Smm	else
754228753Smm		list_size = extattr_list_file(path, namespace, list, list_size);
755228753Smm
756228753Smm	if (list_size == -1) {
757228753Smm		archive_set_error(&a->archive, errno,
758228753Smm			"Couldn't retrieve extended attributes");
759228753Smm		free(list);
760228753Smm		return (ARCHIVE_WARN);
761228753Smm	}
762228753Smm
763228753Smm	p = list;
764228753Smm	while ((p - list) < list_size) {
765228753Smm		size_t len = 255 & (int)*p;
766228753Smm		char *name;
767228753Smm
768368708Smm		if (namespace == EXTATTR_NAMESPACE_SYSTEM) {
769368708Smm			if (!strcmp(p + 1, "nfs4.acl") ||
770368708Smm			    !strcmp(p + 1, "posix1e.acl_access") ||
771368708Smm			    !strcmp(p + 1, "posix1e.acl_default")) {
772368708Smm				p += 1 + len;
773368708Smm				continue;
774368708Smm			}
775368708Smm			strcpy(buff, "system.");
776368708Smm		} else {
777368708Smm			strcpy(buff, "user.");
778368708Smm		}
779228753Smm		name = buff + strlen(buff);
780228753Smm		memcpy(name, p + 1, len);
781228753Smm		name[len] = '\0';
782315433Smm		setup_xattr(a, entry, namespace, name, buff, *fd, path);
783228753Smm		p += 1 + len;
784228753Smm	}
785228753Smm
786228753Smm	free(list);
787228753Smm	return (ARCHIVE_OK);
788228753Smm}
789228753Smm
790368708Smmstatic int
791368708Smmsetup_xattrs(struct archive_read_disk *a,
792368708Smm    struct archive_entry *entry, int *fd)
793368708Smm{
794368708Smm	int namespaces[2];
795368708Smm	int i, res;
796368708Smm
797368708Smm	namespaces[0] = EXTATTR_NAMESPACE_USER;
798368708Smm	namespaces[1] = EXTATTR_NAMESPACE_SYSTEM;
799368708Smm
800368708Smm	for (i = 0; i < 2; i++) {
801368708Smm		res = setup_xattrs_namespace(a, entry, fd,
802368708Smm		    namespaces[i]);
803368708Smm		switch (res) {
804368708Smm			case (ARCHIVE_OK):
805368708Smm			case (ARCHIVE_WARN):
806368708Smm				break;
807368708Smm			default:
808368708Smm				return (res);
809368708Smm		}
810368708Smm	}
811368708Smm
812368708Smm	return (ARCHIVE_OK);
813368708Smm}
814368708Smm
815228753Smm#else
816228753Smm
817228753Smm/*
818228753Smm * Generic (stub) extended attribute support.
819228753Smm */
820228753Smmstatic int
821228753Smmsetup_xattrs(struct archive_read_disk *a,
822238856Smm    struct archive_entry *entry, int *fd)
823228753Smm{
824228753Smm	(void)a;     /* UNUSED */
825228753Smm	(void)entry; /* UNUSED */
826228753Smm	(void)fd;    /* UNUSED */
827228753Smm	return (ARCHIVE_OK);
828228753Smm}
829228753Smm
830228753Smm#endif
831232153Smm
832232153Smm#if defined(HAVE_LINUX_FIEMAP_H)
833232153Smm
834232153Smm/*
835311042Smm * Linux FIEMAP sparse interface.
836232153Smm *
837232153Smm * The FIEMAP ioctl returns an "extent" for each physical allocation
838232153Smm * on disk.  We need to process those to generate a more compact list
839232153Smm * of logical file blocks.  We also need to be very careful to use
840232153Smm * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes
841232153Smm * does not report allocations for newly-written data that hasn't
842232153Smm * been synced to disk.
843232153Smm *
844232153Smm * It's important to return a minimal sparse file list because we want
845232153Smm * to not trigger sparse file extensions if we don't have to, since
846232153Smm * not all readers support them.
847232153Smm */
848232153Smm
849232153Smmstatic int
850311042Smmsetup_sparse_fiemap(struct archive_read_disk *a,
851238856Smm    struct archive_entry *entry, int *fd)
852232153Smm{
853232153Smm	char buff[4096];
854232153Smm	struct fiemap *fm;
855232153Smm	struct fiemap_extent *fe;
856232153Smm	int64_t size;
857302001Smm	int count, do_fiemap, iters;
858232153Smm	int exit_sts = ARCHIVE_OK;
859316338Smm	const char *path;
860232153Smm
861232153Smm	if (archive_entry_filetype(entry) != AE_IFREG
862232153Smm	    || archive_entry_size(entry) <= 0
863232153Smm	    || archive_entry_hardlink(entry) != NULL)
864232153Smm		return (ARCHIVE_OK);
865232153Smm
866238856Smm	if (*fd < 0) {
867316338Smm		path = archive_read_disk_entry_setup_path(a, entry, NULL);
868316338Smm		if (path == NULL)
869316338Smm			return (ARCHIVE_FAILED);
870232153Smm
871238856Smm		if (a->tree != NULL)
872238856Smm			*fd = a->open_on_current_dir(a->tree, path,
873248616Smm				O_RDONLY | O_NONBLOCK | O_CLOEXEC);
874238856Smm		else
875248616Smm			*fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
876238856Smm		if (*fd < 0) {
877232153Smm			archive_set_error(&a->archive, errno,
878232153Smm			    "Can't open `%s'", path);
879232153Smm			return (ARCHIVE_FAILED);
880232153Smm		}
881248616Smm		__archive_ensure_cloexec_flag(*fd);
882232153Smm	}
883232153Smm
884248616Smm	/* Initialize buffer to avoid the error valgrind complains about. */
885248616Smm	memset(buff, 0, sizeof(buff));
886232153Smm	count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe);
887232153Smm	fm = (struct fiemap *)buff;
888232153Smm	fm->fm_start = 0;
889232153Smm	fm->fm_length = ~0ULL;;
890232153Smm	fm->fm_flags = FIEMAP_FLAG_SYNC;
891232153Smm	fm->fm_extent_count = count;
892232153Smm	do_fiemap = 1;
893232153Smm	size = archive_entry_size(entry);
894302001Smm	for (iters = 0; ; ++iters) {
895232153Smm		int i, r;
896232153Smm
897238856Smm		r = ioctl(*fd, FS_IOC_FIEMAP, fm);
898232153Smm		if (r < 0) {
899238856Smm			/* When something error happens, it is better we
900238856Smm			 * should return ARCHIVE_OK because an earlier
901311042Smm			 * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */
902311042Smm			goto exit_setup_sparse_fiemap;
903232153Smm		}
904302001Smm		if (fm->fm_mapped_extents == 0) {
905302001Smm			if (iters == 0) {
906302001Smm				/* Fully sparse file; insert a zero-length "data" entry */
907302001Smm				archive_entry_sparse_add_entry(entry, 0, 0);
908302001Smm			}
909232153Smm			break;
910302001Smm		}
911232153Smm		fe = fm->fm_extents;
912232153Smm		for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) {
913232153Smm			if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
914232153Smm				/* The fe_length of the last block does not
915232153Smm				 * adjust itself to its size files. */
916232153Smm				int64_t length = fe->fe_length;
917232153Smm				if (fe->fe_logical + length > (uint64_t)size)
918232153Smm					length -= fe->fe_logical + length - size;
919232153Smm				if (fe->fe_logical == 0 && length == size) {
920232153Smm					/* This is not sparse. */
921232153Smm					do_fiemap = 0;
922232153Smm					break;
923232153Smm				}
924232153Smm				if (length > 0)
925232153Smm					archive_entry_sparse_add_entry(entry,
926232153Smm					    fe->fe_logical, length);
927232153Smm			}
928232153Smm			if (fe->fe_flags & FIEMAP_EXTENT_LAST)
929232153Smm				do_fiemap = 0;
930232153Smm		}
931232153Smm		if (do_fiemap) {
932232153Smm			fe = fm->fm_extents + fm->fm_mapped_extents -1;
933232153Smm			fm->fm_start = fe->fe_logical + fe->fe_length;
934232153Smm		} else
935232153Smm			break;
936232153Smm	}
937311042Smmexit_setup_sparse_fiemap:
938232153Smm	return (exit_sts);
939232153Smm}
940232153Smm
941311042Smm#if !defined(SEEK_HOLE) || !defined(SEEK_DATA)
942311042Smmstatic int
943311042Smmsetup_sparse(struct archive_read_disk *a,
944311042Smm    struct archive_entry *entry, int *fd)
945311042Smm{
946311042Smm	return setup_sparse_fiemap(a, entry, fd);
947311042Smm}
948311042Smm#endif
949311042Smm#endif	/* defined(HAVE_LINUX_FIEMAP_H) */
950232153Smm
951311042Smm#if defined(SEEK_HOLE) && defined(SEEK_DATA)
952311042Smm
953232153Smm/*
954311042Smm * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris)
955232153Smm */
956232153Smm
957232153Smmstatic int
958232153Smmsetup_sparse(struct archive_read_disk *a,
959238856Smm    struct archive_entry *entry, int *fd)
960232153Smm{
961232153Smm	int64_t size;
962311042Smm	off_t initial_off;
963311042Smm	off_t off_s, off_e;
964232153Smm	int exit_sts = ARCHIVE_OK;
965302001Smm	int check_fully_sparse = 0;
966316338Smm	const char *path;
967232153Smm
968232153Smm	if (archive_entry_filetype(entry) != AE_IFREG
969232153Smm	    || archive_entry_size(entry) <= 0
970232153Smm	    || archive_entry_hardlink(entry) != NULL)
971232153Smm		return (ARCHIVE_OK);
972232153Smm
973232153Smm	/* Does filesystem support the reporting of hole ? */
974318483Smm	if (*fd < 0)
975316338Smm		path = archive_read_disk_entry_setup_path(a, entry, fd);
976318483Smm	else
977318483Smm		path = NULL;
978238856Smm
979238856Smm	if (*fd >= 0) {
980311042Smm#ifdef _PC_MIN_HOLE_SIZE
981238856Smm		if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0)
982232153Smm			return (ARCHIVE_OK);
983311042Smm#endif
984238856Smm		initial_off = lseek(*fd, 0, SEEK_CUR);
985232153Smm		if (initial_off != 0)
986238856Smm			lseek(*fd, 0, SEEK_SET);
987232153Smm	} else {
988318483Smm		if (path == NULL)
989318483Smm			return (ARCHIVE_FAILED);
990311042Smm#ifdef _PC_MIN_HOLE_SIZE
991232153Smm		if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
992232153Smm			return (ARCHIVE_OK);
993311042Smm#endif
994248616Smm		*fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
995238856Smm		if (*fd < 0) {
996232153Smm			archive_set_error(&a->archive, errno,
997232153Smm			    "Can't open `%s'", path);
998232153Smm			return (ARCHIVE_FAILED);
999232153Smm		}
1000248616Smm		__archive_ensure_cloexec_flag(*fd);
1001232153Smm		initial_off = 0;
1002232153Smm	}
1003232153Smm
1004311042Smm#ifndef _PC_MIN_HOLE_SIZE
1005311042Smm	/* Check if the underlying filesystem supports seek hole */
1006311042Smm	off_s = lseek(*fd, 0, SEEK_HOLE);
1007311042Smm	if (off_s < 0)
1008311042Smm#if defined(HAVE_LINUX_FIEMAP_H)
1009311042Smm		return setup_sparse_fiemap(a, entry, fd);
1010311042Smm#else
1011311042Smm		goto exit_setup_sparse;
1012311042Smm#endif
1013311042Smm	else if (off_s > 0)
1014311042Smm		lseek(*fd, 0, SEEK_SET);
1015311042Smm#endif
1016311042Smm
1017232153Smm	off_s = 0;
1018232153Smm	size = archive_entry_size(entry);
1019232153Smm	while (off_s < size) {
1020238856Smm		off_s = lseek(*fd, off_s, SEEK_DATA);
1021232153Smm		if (off_s == (off_t)-1) {
1022302001Smm			if (errno == ENXIO) {
1023302001Smm				/* no more hole */
1024302001Smm				if (archive_entry_sparse_count(entry) == 0) {
1025302001Smm					/* Potentially a fully-sparse file. */
1026302001Smm					check_fully_sparse = 1;
1027302001Smm				}
1028302001Smm				break;
1029302001Smm			}
1030232153Smm			archive_set_error(&a->archive, errno,
1031232153Smm			    "lseek(SEEK_HOLE) failed");
1032232153Smm			exit_sts = ARCHIVE_FAILED;
1033232153Smm			goto exit_setup_sparse;
1034232153Smm		}
1035238856Smm		off_e = lseek(*fd, off_s, SEEK_HOLE);
1036238856Smm		if (off_e == (off_t)-1) {
1037232153Smm			if (errno == ENXIO) {
1038238856Smm				off_e = lseek(*fd, 0, SEEK_END);
1039232153Smm				if (off_e != (off_t)-1)
1040232153Smm					break;/* no more data */
1041232153Smm			}
1042232153Smm			archive_set_error(&a->archive, errno,
1043232153Smm			    "lseek(SEEK_DATA) failed");
1044232153Smm			exit_sts = ARCHIVE_FAILED;
1045232153Smm			goto exit_setup_sparse;
1046232153Smm		}
1047232153Smm		if (off_s == 0 && off_e == size)
1048311042Smm			break;/* This is not sparse. */
1049232153Smm		archive_entry_sparse_add_entry(entry, off_s,
1050232153Smm			off_e - off_s);
1051232153Smm		off_s = off_e;
1052232153Smm	}
1053302001Smm
1054302001Smm	if (check_fully_sparse) {
1055302001Smm		if (lseek(*fd, 0, SEEK_HOLE) == 0 &&
1056302001Smm			lseek(*fd, 0, SEEK_END) == size) {
1057302001Smm			/* Fully sparse file; insert a zero-length "data" entry */
1058302001Smm			archive_entry_sparse_add_entry(entry, 0, 0);
1059302001Smm		}
1060302001Smm	}
1061232153Smmexit_setup_sparse:
1062238856Smm	lseek(*fd, initial_off, SEEK_SET);
1063232153Smm	return (exit_sts);
1064232153Smm}
1065232153Smm
1066311042Smm#elif !defined(HAVE_LINUX_FIEMAP_H)
1067232153Smm
1068232153Smm/*
1069232153Smm * Generic (stub) sparse support.
1070232153Smm */
1071232153Smmstatic int
1072232153Smmsetup_sparse(struct archive_read_disk *a,
1073238856Smm    struct archive_entry *entry, int *fd)
1074232153Smm{
1075232153Smm	(void)a;     /* UNUSED */
1076232153Smm	(void)entry; /* UNUSED */
1077232153Smm	(void)fd;    /* UNUSED */
1078232153Smm	return (ARCHIVE_OK);
1079232153Smm}
1080232153Smm
1081232153Smm#endif
1082232153Smm
1083232153Smm#endif /* !defined(_WIN32) || defined(__CYGWIN__) */
1084232153Smm
1085