1228753Smm/*-
2228753Smm * Copyright (c) 2003-2009 Tim Kientzle
3228753Smm * All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice, this list of conditions and the following disclaimer.
10228753Smm * 2. Redistributions in binary form must reproduce the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer in the
12228753Smm *    documentation and/or other materials provided with the distribution.
13228753Smm *
14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24228753Smm */
25228753Smm
26228753Smm#include "archive_platform.h"
27229592Smm__FBSDID("$FreeBSD$");
28228753Smm
29228753Smm#ifdef HAVE_SYS_TYPES_H
30228753Smm/* Mac OSX requires sys/types.h before sys/acl.h. */
31228753Smm#include <sys/types.h>
32228753Smm#endif
33228753Smm#ifdef HAVE_SYS_ACL_H
34228753Smm#include <sys/acl.h>
35228753Smm#endif
36228753Smm#ifdef HAVE_SYS_EXTATTR_H
37228753Smm#include <sys/extattr.h>
38228753Smm#endif
39228753Smm#ifdef HAVE_SYS_PARAM_H
40228753Smm#include <sys/param.h>
41228753Smm#endif
42228753Smm#ifdef HAVE_SYS_STAT_H
43228753Smm#include <sys/stat.h>
44228753Smm#endif
45228753Smm#ifdef HAVE_SYS_XATTR_H
46228753Smm#include <sys/xattr.h>
47228753Smm#endif
48228753Smm#ifdef HAVE_ACL_LIBACL_H
49228753Smm#include <acl/libacl.h>
50228753Smm#endif
51228753Smm#ifdef HAVE_ATTR_XATTR_H
52228753Smm#include <attr/xattr.h>
53228753Smm#endif
54228753Smm#ifdef HAVE_ERRNO_H
55228753Smm#include <errno.h>
56228753Smm#endif
57228753Smm#ifdef HAVE_LIMITS_H
58228753Smm#include <limits.h>
59228753Smm#endif
60228753Smm#ifdef HAVE_WINDOWS_H
61228753Smm#include <windows.h>
62228753Smm#endif
63228753Smm
64228753Smm#include "archive.h"
65228753Smm#include "archive_entry.h"
66228753Smm#include "archive_private.h"
67228753Smm#include "archive_read_disk_private.h"
68228753Smm
69228753Smm/*
70228753Smm * Linux and FreeBSD plug this obvious hole in POSIX.1e in
71228753Smm * different ways.
72228753Smm */
73228753Smm#if HAVE_ACL_GET_PERM
74228753Smm#define	ACL_GET_PERM acl_get_perm
75228753Smm#elif HAVE_ACL_GET_PERM_NP
76228753Smm#define	ACL_GET_PERM acl_get_perm_np
77228753Smm#endif
78228753Smm
79228753Smmstatic int setup_acls_posix1e(struct archive_read_disk *,
80228753Smm    struct archive_entry *, int fd);
81228753Smmstatic int setup_xattrs(struct archive_read_disk *,
82228753Smm    struct archive_entry *, int fd);
83228753Smm
84228753Smmint
85228753Smmarchive_read_disk_entry_from_file(struct archive *_a,
86228753Smm    struct archive_entry *entry,
87228753Smm    int fd, const struct stat *st)
88228753Smm{
89228753Smm	struct archive_read_disk *a = (struct archive_read_disk *)_a;
90228753Smm	const char *path, *name;
91228753Smm	struct stat s;
92228753Smm	int initial_fd = fd;
93228753Smm	int r, r1;
94228753Smm
95228753Smm	archive_clear_error(_a);
96228753Smm	path = archive_entry_sourcepath(entry);
97228753Smm	if (path == NULL)
98228753Smm		path = archive_entry_pathname(entry);
99228753Smm
100228753Smm#ifdef EXT2_IOC_GETFLAGS
101228753Smm	/* Linux requires an extra ioctl to pull the flags.  Although
102228753Smm	 * this is an extra step, it has a nice side-effect: We get an
103228753Smm	 * open file descriptor which we can use in the subsequent lookups. */
104228753Smm	if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
105228753Smm		if (fd < 0)
106228753Smm			fd = open(pathname, O_RDONLY | O_NONBLOCK | O_BINARY);
107228753Smm		if (fd >= 0) {
108228753Smm			unsigned long stflags;
109228753Smm			int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
110228753Smm			if (r == 0 && stflags != 0)
111228753Smm				archive_entry_set_fflags(entry, stflags, 0);
112228753Smm		}
113228753Smm	}
114228753Smm#endif
115228753Smm
116228753Smm	if (st == NULL) {
117228753Smm		/* TODO: On Windows, use GetFileInfoByHandle() here.
118228753Smm		 * Using Windows stat() call is badly broken, but
119228753Smm		 * even the stat() wrapper has problems because
120228753Smm		 * 'struct stat' is broken on Windows.
121228753Smm		 */
122228753Smm#if HAVE_FSTAT
123228753Smm		if (fd >= 0) {
124228753Smm			if (fstat(fd, &s) != 0) {
125228753Smm				archive_set_error(&a->archive, errno,
126228753Smm				    "Can't fstat");
127228753Smm				return (ARCHIVE_FAILED);
128228753Smm			}
129228753Smm		} else
130228753Smm#endif
131228753Smm#if HAVE_LSTAT
132228753Smm		if (!a->follow_symlinks) {
133228753Smm			if (lstat(path, &s) != 0) {
134228753Smm				archive_set_error(&a->archive, errno,
135228753Smm				    "Can't lstat %s", path);
136228753Smm				return (ARCHIVE_FAILED);
137228753Smm			}
138228753Smm		} else
139228753Smm#endif
140228753Smm		if (stat(path, &s) != 0) {
141228753Smm			archive_set_error(&a->archive, errno,
142228753Smm			    "Can't lstat %s", path);
143228753Smm			return (ARCHIVE_FAILED);
144228753Smm		}
145228753Smm		st = &s;
146228753Smm	}
147228753Smm	archive_entry_copy_stat(entry, st);
148228753Smm
149228753Smm	/* Lookup uname/gname */
150228753Smm	name = archive_read_disk_uname(_a, archive_entry_uid(entry));
151228753Smm	if (name != NULL)
152228753Smm		archive_entry_copy_uname(entry, name);
153228753Smm	name = archive_read_disk_gname(_a, archive_entry_gid(entry));
154228753Smm	if (name != NULL)
155228753Smm		archive_entry_copy_gname(entry, name);
156228753Smm
157228753Smm#ifdef HAVE_STRUCT_STAT_ST_FLAGS
158228753Smm	/* On FreeBSD, we get flags for free with the stat. */
159228753Smm	/* TODO: Does this belong in copy_stat()? */
160228753Smm	if (st->st_flags != 0)
161228753Smm		archive_entry_set_fflags(entry, st->st_flags, 0);
162228753Smm#endif
163228753Smm
164228753Smm#ifdef HAVE_READLINK
165228753Smm	if (S_ISLNK(st->st_mode)) {
166228753Smm		char linkbuffer[PATH_MAX + 1];
167228753Smm		int lnklen = readlink(path, linkbuffer, PATH_MAX);
168228753Smm		if (lnklen < 0) {
169228753Smm			archive_set_error(&a->archive, errno,
170228753Smm			    "Couldn't read link data");
171228753Smm			return (ARCHIVE_FAILED);
172228753Smm		}
173228753Smm		linkbuffer[lnklen] = 0;
174228753Smm		archive_entry_set_symlink(entry, linkbuffer);
175228753Smm	}
176228753Smm#endif
177228753Smm
178228753Smm	r = setup_acls_posix1e(a, entry, fd);
179228753Smm	r1 = setup_xattrs(a, entry, fd);
180228753Smm	if (r1 < r)
181228753Smm		r = r1;
182228753Smm	/* If we opened the file earlier in this function, close it. */
183228753Smm	if (initial_fd != fd)
184228753Smm		close(fd);
185228753Smm	return (r);
186228753Smm}
187228753Smm
188228753Smm#ifdef HAVE_POSIX_ACL
189228753Smmstatic void setup_acl_posix1e(struct archive_read_disk *a,
190228753Smm    struct archive_entry *entry, acl_t acl, int archive_entry_acl_type);
191228753Smm
192228753Smmstatic int
193228753Smmsetup_acls_posix1e(struct archive_read_disk *a,
194228753Smm    struct archive_entry *entry, int fd)
195228753Smm{
196228753Smm	const char	*accpath;
197228753Smm	acl_t		 acl;
198228753Smm
199228753Smm	accpath = archive_entry_sourcepath(entry);
200228753Smm	if (accpath == NULL)
201228753Smm		accpath = archive_entry_pathname(entry);
202228753Smm
203228753Smm	archive_entry_acl_clear(entry);
204228753Smm
205228753Smm	/* Retrieve access ACL from file. */
206228753Smm	if (fd >= 0)
207228753Smm		acl = acl_get_fd(fd);
208228753Smm#if HAVE_ACL_GET_LINK_NP
209228753Smm	else if (!a->follow_symlinks)
210228753Smm		acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
211228753Smm#else
212228753Smm	else if ((!a->follow_symlinks)
213228753Smm	    && (archive_entry_filetype(entry) == AE_IFLNK))
214228753Smm		/* We can't get the ACL of a symlink, so we assume it can't
215228753Smm		   have one. */
216228753Smm		acl = NULL;
217228753Smm#endif
218228753Smm	else
219228753Smm		acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
220228753Smm	if (acl != NULL) {
221228753Smm		setup_acl_posix1e(a, entry, acl,
222228753Smm		    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
223228753Smm		acl_free(acl);
224228753Smm	}
225228753Smm
226228753Smm	/* Only directories can have default ACLs. */
227228753Smm	if (S_ISDIR(archive_entry_mode(entry))) {
228228753Smm		acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
229228753Smm		if (acl != NULL) {
230228753Smm			setup_acl_posix1e(a, entry, acl,
231228753Smm			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
232228753Smm			acl_free(acl);
233228753Smm		}
234228753Smm	}
235228753Smm	return (ARCHIVE_OK);
236228753Smm}
237228753Smm
238228753Smm/*
239228753Smm * Translate POSIX.1e ACL into libarchive internal structure.
240228753Smm */
241228753Smmstatic void
242228753Smmsetup_acl_posix1e(struct archive_read_disk *a,
243228753Smm    struct archive_entry *entry, acl_t acl, int archive_entry_acl_type)
244228753Smm{
245228753Smm	acl_tag_t	 acl_tag;
246228753Smm	acl_entry_t	 acl_entry;
247228753Smm	acl_permset_t	 acl_permset;
248228753Smm	int		 s, ae_id, ae_tag, ae_perm;
249228753Smm	const char	*ae_name;
250228753Smm
251228753Smm	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
252228753Smm	while (s == 1) {
253228753Smm		ae_id = -1;
254228753Smm		ae_name = NULL;
255228753Smm
256228753Smm		acl_get_tag_type(acl_entry, &acl_tag);
257228753Smm		if (acl_tag == ACL_USER) {
258228753Smm			ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry);
259228753Smm			ae_name = archive_read_disk_uname(&a->archive, ae_id);
260228753Smm			ae_tag = ARCHIVE_ENTRY_ACL_USER;
261228753Smm		} else if (acl_tag == ACL_GROUP) {
262228753Smm			ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry);
263228753Smm			ae_name = archive_read_disk_gname(&a->archive, ae_id);
264228753Smm			ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
265228753Smm		} else if (acl_tag == ACL_MASK) {
266228753Smm			ae_tag = ARCHIVE_ENTRY_ACL_MASK;
267228753Smm		} else if (acl_tag == ACL_USER_OBJ) {
268228753Smm			ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
269228753Smm		} else if (acl_tag == ACL_GROUP_OBJ) {
270228753Smm			ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
271228753Smm		} else if (acl_tag == ACL_OTHER) {
272228753Smm			ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
273228753Smm		} else {
274228753Smm			/* Skip types that libarchive can't support. */
275228753Smm			continue;
276228753Smm		}
277228753Smm
278228753Smm		acl_get_permset(acl_entry, &acl_permset);
279228753Smm		ae_perm = 0;
280228753Smm		/*
281228753Smm		 * acl_get_perm() is spelled differently on different
282228753Smm		 * platforms; see above.
283228753Smm		 */
284228753Smm		if (ACL_GET_PERM(acl_permset, ACL_EXECUTE))
285228753Smm			ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE;
286228753Smm		if (ACL_GET_PERM(acl_permset, ACL_READ))
287228753Smm			ae_perm |= ARCHIVE_ENTRY_ACL_READ;
288228753Smm		if (ACL_GET_PERM(acl_permset, ACL_WRITE))
289228753Smm			ae_perm |= ARCHIVE_ENTRY_ACL_WRITE;
290228753Smm
291228753Smm		archive_entry_acl_add_entry(entry,
292228753Smm		    archive_entry_acl_type, ae_perm, ae_tag,
293228753Smm		    ae_id, ae_name);
294228753Smm
295228753Smm		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
296228753Smm	}
297228753Smm}
298228753Smm#else
299228753Smmstatic int
300228753Smmsetup_acls_posix1e(struct archive_read_disk *a,
301228753Smm    struct archive_entry *entry, int fd)
302228753Smm{
303228753Smm	(void)a;      /* UNUSED */
304228753Smm	(void)entry;  /* UNUSED */
305228753Smm	(void)fd;     /* UNUSED */
306228753Smm	return (ARCHIVE_OK);
307228753Smm}
308228753Smm#endif
309228753Smm
310228753Smm#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR
311228753Smm
312228753Smm/*
313228753Smm * Linux extended attribute support.
314228753Smm *
315228753Smm * TODO:  By using a stack-allocated buffer for the first
316228753Smm * call to getxattr(), we might be able to avoid the second
317228753Smm * call entirely.  We only need the second call if the
318228753Smm * stack-allocated buffer is too small.  But a modest buffer
319228753Smm * of 1024 bytes or so will often be big enough.  Same applies
320228753Smm * to listxattr().
321228753Smm */
322228753Smm
323228753Smm
324228753Smmstatic int
325228753Smmsetup_xattr(struct archive_read_disk *a,
326228753Smm    struct archive_entry *entry, const char *name, int fd)
327228753Smm{
328228753Smm	ssize_t size;
329228753Smm	void *value = NULL;
330228753Smm	const char *accpath;
331228753Smm
332228753Smm	(void)fd; /* UNUSED */
333228753Smm
334228753Smm	accpath = archive_entry_sourcepath(entry);
335228753Smm	if (accpath == NULL)
336228753Smm		accpath = archive_entry_pathname(entry);
337228753Smm
338228753Smm	if (!a->follow_symlinks)
339228753Smm		size = lgetxattr(accpath, name, NULL, 0);
340228753Smm	else
341228753Smm		size = getxattr(accpath, name, NULL, 0);
342228753Smm
343228753Smm	if (size == -1) {
344228753Smm		archive_set_error(&a->archive, errno,
345228753Smm		    "Couldn't query extended attribute");
346228753Smm		return (ARCHIVE_WARN);
347228753Smm	}
348228753Smm
349228753Smm	if (size > 0 && (value = malloc(size)) == NULL) {
350228753Smm		archive_set_error(&a->archive, errno, "Out of memory");
351228753Smm		return (ARCHIVE_FATAL);
352228753Smm	}
353228753Smm
354228753Smm	if (!a->follow_symlinks)
355228753Smm		size = lgetxattr(accpath, name, value, size);
356228753Smm	else
357228753Smm		size = getxattr(accpath, name, value, size);
358228753Smm
359228753Smm	if (size == -1) {
360228753Smm		archive_set_error(&a->archive, errno,
361228753Smm		    "Couldn't read extended attribute");
362228753Smm		return (ARCHIVE_WARN);
363228753Smm	}
364228753Smm
365228753Smm	archive_entry_xattr_add_entry(entry, name, value, size);
366228753Smm
367228753Smm	free(value);
368228753Smm	return (ARCHIVE_OK);
369228753Smm}
370228753Smm
371228753Smmstatic int
372228753Smmsetup_xattrs(struct archive_read_disk *a,
373228753Smm    struct archive_entry *entry, int fd)
374228753Smm{
375228753Smm	char *list, *p;
376228753Smm	const char *path;
377228753Smm	ssize_t list_size;
378228753Smm
379228753Smm
380228753Smm	path = archive_entry_sourcepath(entry);
381228753Smm	if (path == NULL)
382228753Smm		path = archive_entry_pathname(entry);
383228753Smm
384228753Smm	if (!a->follow_symlinks)
385228753Smm		list_size = llistxattr(path, NULL, 0);
386228753Smm	else
387228753Smm		list_size = listxattr(path, NULL, 0);
388228753Smm
389228753Smm	if (list_size == -1) {
390228753Smm		if (errno == ENOTSUP)
391228753Smm			return (ARCHIVE_OK);
392228753Smm		archive_set_error(&a->archive, errno,
393228753Smm			"Couldn't list extended attributes");
394228753Smm		return (ARCHIVE_WARN);
395228753Smm	}
396228753Smm
397228753Smm	if (list_size == 0)
398228753Smm		return (ARCHIVE_OK);
399228753Smm
400228753Smm	if ((list = malloc(list_size)) == NULL) {
401228753Smm		archive_set_error(&a->archive, errno, "Out of memory");
402228753Smm		return (ARCHIVE_FATAL);
403228753Smm	}
404228753Smm
405228753Smm	if (!a->follow_symlinks)
406228753Smm		list_size = llistxattr(path, list, list_size);
407228753Smm	else
408228753Smm		list_size = listxattr(path, list, list_size);
409228753Smm
410228753Smm	if (list_size == -1) {
411228753Smm		archive_set_error(&a->archive, errno,
412228753Smm			"Couldn't retrieve extended attributes");
413228753Smm		free(list);
414228753Smm		return (ARCHIVE_WARN);
415228753Smm	}
416228753Smm
417228753Smm	for (p = list; (p - list) < list_size; p += strlen(p) + 1) {
418228753Smm		if (strncmp(p, "system.", 7) == 0 ||
419228753Smm				strncmp(p, "xfsroot.", 8) == 0)
420228753Smm			continue;
421228753Smm		setup_xattr(a, entry, p, fd);
422228753Smm	}
423228753Smm
424228753Smm	free(list);
425228753Smm	return (ARCHIVE_OK);
426228753Smm}
427228753Smm
428228753Smm#elif HAVE_EXTATTR_GET_FILE && HAVE_EXTATTR_LIST_FILE && \
429228753Smm    HAVE_DECL_EXTATTR_NAMESPACE_USER
430228753Smm
431228753Smm/*
432228753Smm * FreeBSD extattr interface.
433228753Smm */
434228753Smm
435228753Smm/* TODO: Implement this.  Follow the Linux model above, but
436228753Smm * with FreeBSD-specific system calls, of course.  Be careful
437228753Smm * to not include the system extattrs that hold ACLs; we handle
438228753Smm * those separately.
439228753Smm */
440228753Smmstatic int
441228753Smmsetup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
442228753Smm    int namespace, const char *name, const char *fullname, int fd);
443228753Smm
444228753Smmstatic int
445228753Smmsetup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
446228753Smm    int namespace, const char *name, const char *fullname, int fd)
447228753Smm{
448228753Smm	ssize_t size;
449228753Smm	void *value = NULL;
450228753Smm	const char *accpath;
451228753Smm
452228753Smm	(void)fd; /* UNUSED */
453228753Smm
454228753Smm	accpath = archive_entry_sourcepath(entry);
455228753Smm	if (accpath == NULL)
456228753Smm		accpath = archive_entry_pathname(entry);
457228753Smm
458228753Smm	if (!a->follow_symlinks)
459228753Smm		size = extattr_get_link(accpath, namespace, name, NULL, 0);
460228753Smm	else
461228753Smm		size = extattr_get_file(accpath, namespace, name, NULL, 0);
462228753Smm
463228753Smm	if (size == -1) {
464228753Smm		archive_set_error(&a->archive, errno,
465228753Smm		    "Couldn't query extended attribute");
466228753Smm		return (ARCHIVE_WARN);
467228753Smm	}
468228753Smm
469228753Smm	if (size > 0 && (value = malloc(size)) == NULL) {
470228753Smm		archive_set_error(&a->archive, errno, "Out of memory");
471228753Smm		return (ARCHIVE_FATAL);
472228753Smm	}
473228753Smm
474228753Smm	if (!a->follow_symlinks)
475228753Smm		size = extattr_get_link(accpath, namespace, name, value, size);
476228753Smm	else
477228753Smm		size = extattr_get_file(accpath, namespace, name, value, size);
478228753Smm
479228753Smm	if (size == -1) {
480228753Smm		archive_set_error(&a->archive, errno,
481228753Smm		    "Couldn't read extended attribute");
482228753Smm		return (ARCHIVE_WARN);
483228753Smm	}
484228753Smm
485228753Smm	archive_entry_xattr_add_entry(entry, fullname, value, size);
486228753Smm
487228753Smm	free(value);
488228753Smm	return (ARCHIVE_OK);
489228753Smm}
490228753Smm
491228753Smmstatic int
492228753Smmsetup_xattrs(struct archive_read_disk *a,
493228753Smm    struct archive_entry *entry, int fd)
494228753Smm{
495228753Smm	char buff[512];
496228753Smm	char *list, *p;
497228753Smm	ssize_t list_size;
498228753Smm	const char *path;
499228753Smm	int namespace = EXTATTR_NAMESPACE_USER;
500228753Smm
501228753Smm	path = archive_entry_sourcepath(entry);
502228753Smm	if (path == NULL)
503228753Smm		path = archive_entry_pathname(entry);
504228753Smm
505228753Smm	if (!a->follow_symlinks)
506228753Smm		list_size = extattr_list_link(path, namespace, NULL, 0);
507228753Smm	else
508228753Smm		list_size = extattr_list_file(path, namespace, NULL, 0);
509228753Smm
510228753Smm	if (list_size == -1 && errno == EOPNOTSUPP)
511228753Smm		return (ARCHIVE_OK);
512228753Smm	if (list_size == -1) {
513228753Smm		archive_set_error(&a->archive, errno,
514228753Smm			"Couldn't list extended attributes");
515228753Smm		return (ARCHIVE_WARN);
516228753Smm	}
517228753Smm
518228753Smm	if (list_size == 0)
519228753Smm		return (ARCHIVE_OK);
520228753Smm
521228753Smm	if ((list = malloc(list_size)) == NULL) {
522228753Smm		archive_set_error(&a->archive, errno, "Out of memory");
523228753Smm		return (ARCHIVE_FATAL);
524228753Smm	}
525228753Smm
526228753Smm	if (!a->follow_symlinks)
527228753Smm		list_size = extattr_list_link(path, namespace, list, list_size);
528228753Smm	else
529228753Smm		list_size = extattr_list_file(path, namespace, list, list_size);
530228753Smm
531228753Smm	if (list_size == -1) {
532228753Smm		archive_set_error(&a->archive, errno,
533228753Smm			"Couldn't retrieve extended attributes");
534228753Smm		free(list);
535228753Smm		return (ARCHIVE_WARN);
536228753Smm	}
537228753Smm
538228753Smm	p = list;
539228753Smm	while ((p - list) < list_size) {
540228753Smm		size_t len = 255 & (int)*p;
541228753Smm		char *name;
542228753Smm
543228753Smm		strcpy(buff, "user.");
544228753Smm		name = buff + strlen(buff);
545228753Smm		memcpy(name, p + 1, len);
546228753Smm		name[len] = '\0';
547228753Smm		setup_xattr(a, entry, namespace, name, buff, fd);
548228753Smm		p += 1 + len;
549228753Smm	}
550228753Smm
551228753Smm	free(list);
552228753Smm	return (ARCHIVE_OK);
553228753Smm}
554228753Smm
555228753Smm#else
556228753Smm
557228753Smm/*
558228753Smm * Generic (stub) extended attribute support.
559228753Smm */
560228753Smmstatic int
561228753Smmsetup_xattrs(struct archive_read_disk *a,
562228753Smm    struct archive_entry *entry, int fd)
563228753Smm{
564228753Smm	(void)a;     /* UNUSED */
565228753Smm	(void)entry; /* UNUSED */
566228753Smm	(void)fd;    /* UNUSED */
567228753Smm	return (ARCHIVE_OK);
568228753Smm}
569228753Smm
570228753Smm#endif
571