1 /*-
2  * Copyright (c) 2003-2010 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "archive_platform.h"
28 __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk.c 201159 2009-12-29 05:35:40Z kientzle $");
29 
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33 #ifdef HAVE_SYS_ACL_H
34 #define _ACL_PRIVATE /* For debugging */
35 #include <sys/acl.h>
36 #endif
37 #if HAVE_DARWIN_ACL
38 #include <membership.h>
39 #endif
40 #ifdef HAVE_ERRNO_H
41 #include <errno.h>
42 #endif
43 
44 #include "archive.h"
45 #include "archive_entry.h"
46 #include "archive_acl_private.h"
47 #include "archive_write_disk_private.h"
48 
49 #if !HAVE_POSIX_ACL && !HAVE_NFS4_ACL
50 /* Default empty function body to satisfy mainline code. */
51 int
archive_write_disk_set_acls(struct archive * a,int fd,const char * name,struct archive_acl * abstract_acl)52 archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
53 	 struct archive_acl *abstract_acl)
54 {
55 	(void)a; /* UNUSED */
56 	(void)fd; /* UNUSED */
57 	(void)name; /* UNUSED */
58 	(void)abstract_acl; /* UNUSED */
59 	return (ARCHIVE_OK);
60 }
61 
62 #else /* HAVE_POSIX_ACL || HAVE_NFS4_ACL */
63 
64 #if HAVE_SUN_ACL
65 #define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACE_T
66 #elif HAVE_DARWIN_ACL
67 #define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACL_TYPE_EXTENDED
68 #elif HAVE_ACL_TYPE_NFS4
69 #define	ARCHIVE_PLATFORM_ACL_TYPE_NFS4	ACL_TYPE_NFS4
70 #endif
71 
72 static int	set_acl(struct archive *, int fd, const char *,
73 			struct archive_acl *,
74 			acl_type_t, int archive_entry_acl_type, const char *tn);
75 
76 int
archive_write_disk_set_acls(struct archive * a,int fd,const char * name,struct archive_acl * abstract_acl)77 archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
78 	 struct archive_acl *abstract_acl)
79 {
80 	int		ret = ARCHIVE_OK;
81 
82 #if !HAVE_DARWIN_ACL
83 	if ((archive_acl_types(abstract_acl)
84 	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
85 #if HAVE_SUN_ACL
86 		/* Solaris writes POSIX.1e access and default ACLs together */
87 		ret = set_acl(a, fd, name, abstract_acl, ACLENT_T,
88 		    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e");
89 #else	/* HAVE_POSIX_ACL */
90 		if ((archive_acl_types(abstract_acl)
91 		    & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
92 			ret = set_acl(a, fd, name, abstract_acl,
93 			    ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
94 			    "access");
95 			if (ret != ARCHIVE_OK)
96 				return (ret);
97 		}
98 		if ((archive_acl_types(abstract_acl)
99 		    & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
100 			ret = set_acl(a, fd, name, abstract_acl,
101 			    ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT,
102 			    "default");
103 #endif	/* !HAVE_SUN_ACL */
104 		/* Simultaneous POSIX.1e and NFSv4 is not supported */
105 		return (ret);
106 	}
107 #endif	/* !HAVE_DARWIN_ACL */
108 #if HAVE_NFS4_ACL
109 	if ((archive_acl_types(abstract_acl) &
110 	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
111 		ret = set_acl(a, fd, name, abstract_acl,
112 		    ARCHIVE_PLATFORM_ACL_TYPE_NFS4,
113 		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
114 	}
115 #endif	/* HAVE_NFS4_ACL */
116 	return (ret);
117 }
118 
119 /*
120  * Translate system ACL permissions into libarchive internal structure
121  */
122 static const struct {
123 	const int archive_perm;
124 	const int platform_perm;
125 } acl_perm_map[] = {
126 #if HAVE_SUN_ACL	/* Solaris NFSv4 ACL permissions */
127 	{ARCHIVE_ENTRY_ACL_EXECUTE, ACE_EXECUTE},
128 	{ARCHIVE_ENTRY_ACL_READ_DATA, ACE_READ_DATA},
129 	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACE_LIST_DIRECTORY},
130 	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACE_WRITE_DATA},
131 	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACE_ADD_FILE},
132 	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACE_APPEND_DATA},
133 	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACE_ADD_SUBDIRECTORY},
134 	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACE_READ_NAMED_ATTRS},
135 	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACE_WRITE_NAMED_ATTRS},
136 	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACE_DELETE_CHILD},
137 	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACE_READ_ATTRIBUTES},
138 	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACE_WRITE_ATTRIBUTES},
139 	{ARCHIVE_ENTRY_ACL_DELETE, ACE_DELETE},
140 	{ARCHIVE_ENTRY_ACL_READ_ACL, ACE_READ_ACL},
141 	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACE_WRITE_ACL},
142 	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACE_WRITE_OWNER},
143 	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACE_SYNCHRONIZE}
144 #elif HAVE_DARWIN_ACL	/* MacOS ACL permissions */
145 	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
146 	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
147 	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
148 	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
149 	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
150 	{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
151 	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
152 	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
153 	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
154 	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
155 	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
156 	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES},
157 	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES},
158 	{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY},
159 	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY},
160 	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER},
161 	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
162 #else	/* POSIX.1e ACL permissions */
163 	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
164 	{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
165 	{ARCHIVE_ENTRY_ACL_READ, ACL_READ},
166 #if HAVE_ACL_TYPE_NFS4	/* FreeBSD NFSv4 ACL permissions */
167 	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
168 	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
169 	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
170 	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
171 	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
172 	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
173 	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS},
174 	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS},
175 	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
176 	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
177 	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
178 	{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
179 	{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL},
180 	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL},
181 	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},
182 	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
183 #endif
184 #endif	/* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */
185 };
186 
187 #if HAVE_NFS4_ACL
188 /*
189  * Translate system NFSv4 inheritance flags into libarchive internal structure
190  */
191 static const struct {
192 	const int archive_inherit;
193 	const int platform_inherit;
194 } acl_inherit_map[] = {
195 #if HAVE_SUN_ACL	/* Solaris NFSv4 inheritance flags */
196 	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE},
197 	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE},
198 	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACE_NO_PROPAGATE_INHERIT_ACE},
199 	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACE_INHERIT_ONLY_ACE},
200 	{ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACE_SUCCESSFUL_ACCESS_ACE_FLAG},
201 	{ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACE_FAILED_ACCESS_ACE_FLAG},
202 	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE}
203 #elif HAVE_DARWIN_ACL	/* MacOS NFSv4 inheritance flags */
204 	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED},
205 	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
206 	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
207 	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT},
208 	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT}
209 #else	/* FreeBSD NFSv4 ACL inheritance flags */
210 	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
211 	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
212 	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT},
213 	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY},
214 	{ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS},
215 	{ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS},
216 	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
217 #endif	/* !HAVE_SUN_ACL && !HAVE_DARWIN_ACL */
218 };
219 #endif	/* HAVE_NFS4_ACL */
220 
221 static int
set_acl(struct archive * a,int fd,const char * name,struct archive_acl * abstract_acl,acl_type_t acl_type,int ae_requested_type,const char * tname)222 set_acl(struct archive *a, int fd, const char *name,
223     struct archive_acl *abstract_acl,
224     acl_type_t acl_type, int ae_requested_type, const char *tname)
225 {
226 #if HAVE_SUN_ACL
227 	aclent_t	 *aclent;
228 	ace_t		 *ace;
229 	int		 e, r;
230 	acl_t		 *acl;
231 #else
232 	acl_t		 acl;
233 	acl_entry_t	 acl_entry;
234 	acl_permset_t	 acl_permset;
235 #if HAVE_ACL_TYPE_NFS4 || HAVE_DARWIN_ACL
236 	acl_flagset_t	 acl_flagset;
237 #endif
238 #endif	/* HAVE_SUN_ACL */
239 #if HAVE_ACL_TYPE_NFS4
240 	int		r;
241 #endif
242 	int		 ret;
243 	int		 ae_type, ae_permset, ae_tag, ae_id;
244 #if HAVE_DARWIN_ACL
245 	uuid_t		ae_uuid;
246 #endif
247 	uid_t		 ae_uid;
248 	gid_t		 ae_gid;
249 	const char	*ae_name;
250 	int		 entries;
251 	int		 i;
252 
253 	ret = ARCHIVE_OK;
254 	entries = archive_acl_reset(abstract_acl, ae_requested_type);
255 	if (entries == 0)
256 		return (ARCHIVE_OK);
257 
258 #if HAVE_SUN_ACL
259 	acl = NULL;
260 	acl = malloc(sizeof(acl_t));
261 	if (acl == NULL) {
262 		archive_set_error(a, ARCHIVE_ERRNO_MISC,
263 			"Invalid ACL type");
264 		return (ARCHIVE_FAILED);
265 	}
266 	if (acl_type == ACE_T)
267 		acl->acl_entry_size = sizeof(ace_t);
268 	else if (acl_type == ACLENT_T)
269 		acl->acl_entry_size = sizeof(aclent_t);
270 	else {
271 		archive_set_error(a, ARCHIVE_ERRNO_MISC,
272 			"Invalid ACL type");
273 		acl_free(acl);
274 		return (ARCHIVE_FAILED);
275 	}
276 	acl->acl_type = acl_type;
277 	acl->acl_cnt = entries;
278 
279 	acl->acl_aclp = malloc(entries * acl->acl_entry_size);
280 	if (acl->acl_aclp == NULL) {
281 		archive_set_error(a, errno,
282 		    "Can't allocate memory for acl buffer");
283 		acl_free(acl);
284 		return (ARCHIVE_FAILED);
285 	}
286 #else	/* !HAVE_SUN_ACL */
287 	acl = acl_init(entries);
288 	if (acl == (acl_t)NULL) {
289 		archive_set_error(a, errno,
290 		    "Failed to initialize ACL working storage");
291 		return (ARCHIVE_FAILED);
292 	}
293 #endif	/* !HAVE_SUN_ACL */
294 #if HAVE_SUN_ACL
295 	e = 0;
296 #endif
297 	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
298 		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
299 #if HAVE_SUN_ACL
300 		ace = NULL;
301 		aclent = NULL;
302 		if (acl->acl_type == ACE_T)  {
303 			ace = &((ace_t *)acl->acl_aclp)[e];
304 			ace->a_who = -1;
305 			ace->a_access_mask = 0;
306 			ace->a_flags = 0;
307 		} else {
308 			aclent = &((aclent_t *)acl->acl_aclp)[e];
309 			aclent->a_id = -1;
310 			aclent->a_type = 0;
311 			aclent->a_perm = 0;
312 		}
313 #else	/* !HAVE_SUN_ACL  */
314 #if HAVE_DARWIN_ACL
315 		/*
316 		 * Mac OS doesn't support NFSv4 ACLs for
317 		 * owner@, group@ and everyone@.
318 		 * We skip any of these ACLs found.
319 		 */
320 		if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ ||
321 		    ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ ||
322 		    ae_tag == ARCHIVE_ENTRY_ACL_EVERYONE)
323 			continue;
324 #endif
325 		if (acl_create_entry(&acl, &acl_entry) != 0) {
326 			archive_set_error(a, errno,
327 			    "Failed to create a new ACL entry");
328 			ret = ARCHIVE_FAILED;
329 			goto exit_free;
330 		}
331 #endif	/* !HAVE_SUN_ACL */
332 #if HAVE_DARWIN_ACL
333 		switch (ae_type) {
334 		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
335 			acl_set_tag_type(acl_entry, ACL_EXTENDED_ALLOW);
336 			break;
337 		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
338 			acl_set_tag_type(acl_entry, ACL_EXTENDED_DENY);
339 			break;
340 		default:
341 			/* We don't support any other types on MacOS */
342 			continue;
343 		}
344 #endif
345 		switch (ae_tag) {
346 #if HAVE_SUN_ACL
347 		case ARCHIVE_ENTRY_ACL_USER:
348 			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
349 			if (acl->acl_type == ACE_T)
350 				ace->a_who = ae_uid;
351 			else {
352 				aclent->a_id = ae_uid;
353 				aclent->a_type |= USER;
354 			}
355 			break;
356 		case ARCHIVE_ENTRY_ACL_GROUP:
357 			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
358 			if (acl->acl_type == ACE_T) {
359 				ace->a_who = ae_gid;
360 				ace->a_flags |= ACE_IDENTIFIER_GROUP;
361 			} else {
362 				aclent->a_id = ae_gid;
363 				aclent->a_type |= GROUP;
364 			}
365 			break;
366 		case ARCHIVE_ENTRY_ACL_USER_OBJ:
367 			if (acl->acl_type == ACE_T)
368 				ace->a_flags |= ACE_OWNER;
369 			else
370 				aclent->a_type |= USER_OBJ;
371 			break;
372 		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
373 			if (acl->acl_type == ACE_T) {
374 				ace->a_flags |= ACE_GROUP;
375 				ace->a_flags |= ACE_IDENTIFIER_GROUP;
376 			} else
377 				aclent->a_type |= GROUP_OBJ;
378 			break;
379 		case ARCHIVE_ENTRY_ACL_MASK:
380 			aclent->a_type |= CLASS_OBJ;
381 			break;
382 		case ARCHIVE_ENTRY_ACL_OTHER:
383 			aclent->a_type |= OTHER_OBJ;
384 			break;
385 		case ARCHIVE_ENTRY_ACL_EVERYONE:
386 			ace->a_flags |= ACE_EVERYONE;
387 			break;
388 #else	/* !HAVE_SUN_ACL */
389 		case ARCHIVE_ENTRY_ACL_USER:
390 			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
391 #if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
392 			acl_set_tag_type(acl_entry, ACL_USER);
393 			acl_set_qualifier(acl_entry, &ae_uid);
394 #else	/* MacOS */
395 			if (mbr_identifier_to_uuid(ID_TYPE_UID, &ae_uid,
396 			    sizeof(uid_t), ae_uuid) != 0)
397 				continue;
398 			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
399 				continue;
400 #endif	/* HAVE_DARWIN_ACL */
401 			break;
402 		case ARCHIVE_ENTRY_ACL_GROUP:
403 			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
404 #if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
405 			acl_set_tag_type(acl_entry, ACL_GROUP);
406 			acl_set_qualifier(acl_entry, &ae_gid);
407 #else	/* MacOS */
408 			if (mbr_identifier_to_uuid(ID_TYPE_GID, &ae_gid,
409 			    sizeof(gid_t), ae_uuid) != 0)
410 				continue;
411 			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
412 				continue;
413 #endif	/* HAVE_DARWIN_ACL */
414 			break;
415 #if !HAVE_DARWIN_ACL	/* FreeBSD, Linux */
416 		case ARCHIVE_ENTRY_ACL_USER_OBJ:
417 			acl_set_tag_type(acl_entry, ACL_USER_OBJ);
418 			break;
419 		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
420 			acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
421 			break;
422 		case ARCHIVE_ENTRY_ACL_MASK:
423 			acl_set_tag_type(acl_entry, ACL_MASK);
424 			break;
425 		case ARCHIVE_ENTRY_ACL_OTHER:
426 			acl_set_tag_type(acl_entry, ACL_OTHER);
427 			break;
428 #if HAVE_ACL_TYPE_NFS4	/* FreeBSD only */
429 		case ARCHIVE_ENTRY_ACL_EVERYONE:
430 			acl_set_tag_type(acl_entry, ACL_EVERYONE);
431 			break;
432 #endif
433 #endif	/* !HAVE_DARWIN_ACL */
434 #endif	/* !HAVE_SUN_ACL */
435 		default:
436 			archive_set_error(a, ARCHIVE_ERRNO_MISC,
437 			    "Unknown ACL tag");
438 			ret = ARCHIVE_FAILED;
439 			goto exit_free;
440 		}
441 
442 #if HAVE_ACL_TYPE_NFS4 || HAVE_SUN_ACL
443 		r = 0;
444 		switch (ae_type) {
445 #if HAVE_SUN_ACL
446 		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
447 			if (ace != NULL)
448 				ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
449 			else
450 				r = -1;
451 			break;
452 		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
453 			if (ace != NULL)
454 				ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
455 			else
456 				r = -1;
457 			break;
458 		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
459 			if (ace != NULL)
460 				ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE;
461 			else
462 				r = -1;
463 			break;
464 		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
465 			if (ace != NULL)
466 				ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE;
467 			else
468 				r = -1;
469 			break;
470 		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
471 			if (aclent == NULL)
472 				r = -1;
473 			break;
474 		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
475 			if (aclent != NULL)
476 				aclent->a_type |= ACL_DEFAULT;
477 			else
478 				r = -1;
479 			break;
480 #else	/* !HAVE_SUN_ACL */
481 		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
482 			r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW);
483 			break;
484 		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
485 			r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_DENY);
486 			break;
487 		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
488 			r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_AUDIT);
489 			break;
490 		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
491 			r = acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALARM);
492 			break;
493 		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
494 		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
495 			// These don't translate directly into the system ACL.
496 			break;
497 #endif	/* !HAVE_SUN_ACL */
498 		default:
499 			archive_set_error(a, ARCHIVE_ERRNO_MISC,
500 			    "Unknown ACL entry type");
501 			ret = ARCHIVE_FAILED;
502 			goto exit_free;
503 		}
504 
505 		if (r != 0) {
506 #if HAVE_SUN_ACL
507 			errno = EINVAL;
508 #endif
509 			archive_set_error(a, errno,
510 			    "Failed to set ACL entry type");
511 			ret = ARCHIVE_FAILED;
512 			goto exit_free;
513 		}
514 #endif	/* HAVE_ACL_TYPE_NFS4 || HAVE_SUN_ACL */
515 
516 #if HAVE_SUN_ACL
517 		if (acl->acl_type == ACLENT_T) {
518 			if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE)
519 				aclent->a_perm |= 1;
520 			if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE)
521 				aclent->a_perm |= 2;
522 			if (ae_permset & ARCHIVE_ENTRY_ACL_READ)
523 				aclent->a_perm |= 4;
524 		} else
525 #else
526 		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
527 			archive_set_error(a, errno,
528 			    "Failed to get ACL permission set");
529 			ret = ARCHIVE_FAILED;
530 			goto exit_free;
531 		}
532 		if (acl_clear_perms(acl_permset) != 0) {
533 			archive_set_error(a, errno,
534 			    "Failed to clear ACL permissions");
535 			ret = ARCHIVE_FAILED;
536 			goto exit_free;
537 		}
538 #endif	/* !HAVE_SUN_ACL */
539 		for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) {
540 			if (ae_permset & acl_perm_map[i].archive_perm) {
541 #if HAVE_SUN_ACL
542 				ace->a_access_mask |=
543 				    acl_perm_map[i].platform_perm;
544 #else
545 				if (acl_add_perm(acl_permset,
546 				    acl_perm_map[i].platform_perm) != 0) {
547 					archive_set_error(a, errno,
548 					    "Failed to add ACL permission");
549 					ret = ARCHIVE_FAILED;
550 					goto exit_free;
551 				}
552 #endif
553 			}
554 		}
555 
556 #if HAVE_NFS4_ACL
557 #if HAVE_SUN_ACL
558 		if (acl_type == ACE_T)
559 #elif HAVE_DARWIN_ACL
560 		if (acl_type == ACL_TYPE_EXTENDED)
561 #else	/* FreeBSD */
562 		if (acl_type == ACL_TYPE_NFS4)
563 #endif
564 		{
565 #if HAVE_POSIX_ACL || HAVE_DARWIN_ACL
566 			/*
567 			 * acl_get_flagset_np() fails with non-NFSv4 ACLs
568 			 */
569 			if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
570 				archive_set_error(a, errno,
571 				    "Failed to get flagset from an NFSv4 ACL entry");
572 				ret = ARCHIVE_FAILED;
573 				goto exit_free;
574 			}
575 			if (acl_clear_flags_np(acl_flagset) != 0) {
576 				archive_set_error(a, errno,
577 				    "Failed to clear flags from an NFSv4 ACL flagset");
578 				ret = ARCHIVE_FAILED;
579 				goto exit_free;
580 			}
581 #endif /* HAVE_POSIX_ACL || HAVE_DARWIN_ACL */
582 			for (i = 0; i < (int)(sizeof(acl_inherit_map) /sizeof(acl_inherit_map[0])); ++i) {
583 				if (ae_permset & acl_inherit_map[i].archive_inherit) {
584 #if HAVE_SUN_ACL
585 					ace->a_flags |=
586 					    acl_inherit_map[i].platform_inherit;
587 #else	/* !HAVE_SUN_ACL */
588 					if (acl_add_flag_np(acl_flagset,
589 							acl_inherit_map[i].platform_inherit) != 0) {
590 						archive_set_error(a, errno,
591 						    "Failed to add flag to NFSv4 ACL flagset");
592 						ret = ARCHIVE_FAILED;
593 						goto exit_free;
594 					}
595 #endif	/* HAVE_SUN_ACL */
596 				}
597 			}
598 		}
599 #endif	/* HAVE_NFS4_ACL */
600 #if HAVE_SUN_ACL
601 	e++;
602 #endif
603 	}
604 
605 #if HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL
606 	/* Try restoring the ACL through 'fd' if we can. */
607 #if HAVE_SUN_ACL || HAVE_ACL_SET_FD_NP
608 	if (fd >= 0)
609 #else	/* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */
610 	if (fd >= 0 && acl_type == ACL_TYPE_ACCESS)
611 #endif
612 	{
613 #if HAVE_SUN_ACL
614 		if (facl_set(fd, acl) == 0)
615 #elif HAVE_ACL_SET_FD_NP
616 		if (acl_set_fd_np(fd, acl, acl_type) == 0)
617 #else	/* !HAVE_SUN_ACL && !HAVE_ACL_SET_FD_NP */
618 		if (acl_set_fd(fd, acl) == 0)
619 #endif
620 			ret = ARCHIVE_OK;
621 		else {
622 			if (errno == EOPNOTSUPP) {
623 				/* Filesystem doesn't support ACLs */
624 				ret = ARCHIVE_OK;
625 			} else {
626 				archive_set_error(a, errno,
627 				    "Failed to set %s acl on fd", tname);
628 			}
629 		}
630 	} else
631 #endif	/* HAVE_ACL_SET_FD_NP || HAVE_ACL_SET_FD || HAVE_SUN_ACL */
632 #if HAVE_SUN_ACL
633 	if (acl_set(name, acl) != 0)
634 #elif HAVE_ACL_SET_LINK_NP
635 	if (acl_set_link_np(name, acl_type, acl) != 0)
636 #else
637 	/* TODO: Skip this if 'name' is a symlink. */
638 	if (acl_set_file(name, acl_type, acl) != 0)
639 #endif
640 	{
641 		if (errno == EOPNOTSUPP) {
642 			/* Filesystem doesn't support ACLs */
643 			ret = ARCHIVE_OK;
644 		} else {
645 			archive_set_error(a, errno, "Failed to set %s acl",
646 			    tname);
647 			ret = ARCHIVE_WARN;
648 		}
649 	}
650 exit_free:
651 	acl_free(acl);
652 	return (ret);
653 }
654 #endif	/* HAVE_POSIX_ACL || HAVE_NFS4_ACL */
655