]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/archive_disk_acl_freebsd.c
MFH r328332:
[FreeBSD/stable/10.git] / contrib / libarchive / libarchive / archive_disk_acl_freebsd.c
1 /*-
2  * Copyright (c) 2003-2009 Tim Kientzle
3  * Copyright (c) 2010-2012 Michihiro NAKAJIMA
4  * Copyright (c) 2017 Martin Matuska
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "archive_platform.h"
29
30 #if ARCHIVE_ACL_FREEBSD
31
32 #ifdef HAVE_ERRNO_H
33 #include <errno.h>
34 #endif
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif
38 #ifdef HAVE_SYS_TYPES_H
39 #include <sys/types.h>
40 #endif
41 #ifdef HAVE_SYS_ACL_H
42 #define _ACL_PRIVATE /* For debugging */
43 #include <sys/acl.h>
44 #endif
45
46 #include "archive_entry.h"
47 #include "archive_private.h"
48 #include "archive_read_disk_private.h"
49 #include "archive_write_disk_private.h"
50
51 typedef struct {
52         const int a_perm;       /* Libarchive permission or flag */
53         const int p_perm;       /* Platform permission or flag */
54 } acl_perm_map_t;
55
56 static const acl_perm_map_t acl_posix_perm_map[] = {
57         {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
58         {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
59         {ARCHIVE_ENTRY_ACL_READ, ACL_READ},
60 };
61
62 static const int acl_posix_perm_map_size =
63     (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));
64
65 #if ARCHIVE_ACL_FREEBSD_NFS4
66 static const acl_perm_map_t acl_nfs4_perm_map[] = {
67         {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
68         {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
69         {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
70         {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
71         {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
72         {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
73         {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
74         {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS},
75         {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS},
76         {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
77         {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
78         {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
79         {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
80         {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL},
81         {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL},
82         {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},
83         {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
84 };
85
86 static const int acl_nfs4_perm_map_size =
87     (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
88
89 static const acl_perm_map_t acl_nfs4_flag_map[] = {
90         {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
91         {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
92         {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT},
93         {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY},
94         {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS},
95         {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS},
96 #ifdef ACL_ENTRY_INHERITED
97         {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
98 #endif
99 };
100
101 static const int acl_nfs4_flag_map_size =
102     (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
103 #endif /* ARCHIVE_ACL_FREEBSD_NFS4 */
104
105 static int
106 translate_acl(struct archive_read_disk *a,
107     struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
108 {
109 #if ARCHIVE_ACL_FREEBSD_NFS4
110         int brand;
111         acl_flagset_t    acl_flagset;
112         acl_entry_type_t acl_type;
113 #endif
114         acl_tag_t        acl_tag;
115         acl_entry_t      acl_entry;
116         acl_permset_t    acl_permset;
117         int              i, entry_acl_type, perm_map_size;
118         const acl_perm_map_t    *perm_map;
119         int              r, s, ae_id, ae_tag, ae_perm;
120         void            *q;
121         const char      *ae_name;
122
123 #if ARCHIVE_ACL_FREEBSD_NFS4
124         // FreeBSD "brands" ACLs as POSIX.1e or NFSv4
125         // Make sure the "brand" on this ACL is consistent
126         // with the default_entry_acl_type bits provided.
127         if (acl_get_brand_np(acl, &brand) != 0) {
128                 archive_set_error(&a->archive, errno,
129                     "Failed to read ACL brand");
130                 return (ARCHIVE_WARN);
131         }
132         switch (brand) {
133         case ACL_BRAND_POSIX:
134                 switch (default_entry_acl_type) {
135                 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
136                 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
137                         break;
138                 default:
139                         archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
140                             "Invalid ACL entry type for POSIX.1e ACL");
141                         return (ARCHIVE_WARN);
142                 }
143                 break;
144         case ACL_BRAND_NFS4:
145                 if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
146                         archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
147                             "Invalid ACL entry type for NFSv4 ACL");
148                         return (ARCHIVE_WARN);
149                 }
150                 break;
151         default:
152                 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
153                     "Unknown ACL brand");
154                 return (ARCHIVE_WARN);
155         }
156 #endif
157
158         s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
159         if (s == -1) {
160                 archive_set_error(&a->archive, errno,
161                     "Failed to get first ACL entry");
162                 return (ARCHIVE_WARN);
163         }
164
165         while (s == 1) {
166                 ae_id = -1;
167                 ae_name = NULL;
168                 ae_perm = 0;
169
170                 if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {
171                         archive_set_error(&a->archive, errno,
172                             "Failed to get ACL tag type");
173                         return (ARCHIVE_WARN);
174                 }
175                 switch (acl_tag) {
176                 case ACL_USER:
177                         q = acl_get_qualifier(acl_entry);
178                         if (q != NULL) {
179                                 ae_id = (int)*(uid_t *)q;
180                                 acl_free(q);
181                                 ae_name = archive_read_disk_uname(&a->archive,
182                                     ae_id);
183                         }
184                         ae_tag = ARCHIVE_ENTRY_ACL_USER;
185                         break;
186                 case ACL_GROUP:
187                         q = acl_get_qualifier(acl_entry);
188                         if (q != NULL) {
189                                 ae_id = (int)*(gid_t *)q;
190                                 acl_free(q);
191                                 ae_name = archive_read_disk_gname(&a->archive,
192                                     ae_id);
193                         }
194                         ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
195                         break;
196                 case ACL_MASK:
197                         ae_tag = ARCHIVE_ENTRY_ACL_MASK;
198                         break;
199                 case ACL_USER_OBJ:
200                         ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
201                         break;
202                 case ACL_GROUP_OBJ:
203                         ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
204                         break;
205                 case ACL_OTHER:
206                         ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
207                         break;
208 #if ARCHIVE_ACL_FREEBSD_NFS4
209                 case ACL_EVERYONE:
210                         ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
211                         break;
212 #endif
213                 default:
214                         /* Skip types that libarchive can't support. */
215                         s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
216                         continue;
217                 }
218
219                 // XXX acl_type maps to allow/deny/audit/YYYY bits
220                 entry_acl_type = default_entry_acl_type;
221
222 #if ARCHIVE_ACL_FREEBSD_NFS4
223                 if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
224                         /*
225                          * acl_get_entry_type_np() fails with non-NFSv4 ACLs
226                          */
227                         if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) {
228                                 archive_set_error(&a->archive, errno, "Failed "
229                                     "to get ACL type from a NFSv4 ACL entry");
230                                 return (ARCHIVE_WARN);
231                         }
232                         switch (acl_type) {
233                         case ACL_ENTRY_TYPE_ALLOW:
234                                 entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
235                                 break;
236                         case ACL_ENTRY_TYPE_DENY:
237                                 entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
238                                 break;
239                         case ACL_ENTRY_TYPE_AUDIT:
240                                 entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
241                                 break;
242                         case ACL_ENTRY_TYPE_ALARM:
243                                 entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
244                                 break;
245                         default:
246                                 archive_set_error(&a->archive, errno,
247                                     "Invalid NFSv4 ACL entry type");
248                                 return (ARCHIVE_WARN);
249                         }
250
251                         /*
252                          * Libarchive stores "flag" (NFSv4 inheritance bits)
253                          * in the ae_perm bitmap.
254                          *
255                          * acl_get_flagset_np() fails with non-NFSv4 ACLs
256                          */
257                         if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
258                                 archive_set_error(&a->archive, errno,
259                                     "Failed to get flagset from a NFSv4 "
260                                     "ACL entry");
261                                 return (ARCHIVE_WARN);
262                         }
263                         for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
264                                 r = acl_get_flag_np(acl_flagset,
265                                     acl_nfs4_flag_map[i].p_perm);
266                                 if (r == -1) {
267                                         archive_set_error(&a->archive, errno,
268                                             "Failed to check flag in a NFSv4 "
269                                             "ACL flagset");
270                                         return (ARCHIVE_WARN);
271                                 } else if (r)
272                                         ae_perm |= acl_nfs4_flag_map[i].a_perm;
273                         }
274                 }
275 #endif
276
277                 if (acl_get_permset(acl_entry, &acl_permset) != 0) {
278                         archive_set_error(&a->archive, errno,
279                             "Failed to get ACL permission set");
280                         return (ARCHIVE_WARN);
281                 }
282
283 #if ARCHIVE_ACL_FREEBSD_NFS4
284                 if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
285                         perm_map_size = acl_nfs4_perm_map_size;
286                         perm_map = acl_nfs4_perm_map;
287                 } else {
288 #endif
289                         perm_map_size = acl_posix_perm_map_size;
290                         perm_map = acl_posix_perm_map;
291 #if ARCHIVE_ACL_FREEBSD_NFS4
292                 }
293 #endif
294
295                 for (i = 0; i < perm_map_size; ++i) {
296                         r = acl_get_perm_np(acl_permset, perm_map[i].p_perm);
297                         if (r == -1) {
298                                 archive_set_error(&a->archive, errno,
299                                     "Failed to check permission in an ACL "
300                                     "permission set");
301                                 return (ARCHIVE_WARN);
302                         } else if (r)
303                                 ae_perm |= perm_map[i].a_perm;
304                 }
305
306                 archive_entry_acl_add_entry(entry, entry_acl_type,
307                                             ae_perm, ae_tag,
308                                             ae_id, ae_name);
309
310                 s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
311                 if (s == -1) {
312                         archive_set_error(&a->archive, errno,
313                             "Failed to get next ACL entry");
314                         return (ARCHIVE_WARN);
315                 }
316         }
317         return (ARCHIVE_OK);
318 }
319
320 static int
321 set_acl(struct archive *a, int fd, const char *name,
322     struct archive_acl *abstract_acl,
323     int ae_requested_type, const char *tname)
324 {
325         int              acl_type = 0;
326         acl_t            acl;
327         acl_entry_t      acl_entry;
328         acl_permset_t    acl_permset;
329 #if ARCHIVE_ACL_FREEBSD_NFS4
330         acl_flagset_t    acl_flagset;
331         int              r;
332 #endif
333         int              ret;
334         int              ae_type, ae_permset, ae_tag, ae_id;
335         int              perm_map_size;
336         const acl_perm_map_t    *perm_map;
337         uid_t            ae_uid;
338         gid_t            ae_gid;
339         const char      *ae_name;
340         int              entries;
341         int              i;
342
343         ret = ARCHIVE_OK;
344         entries = archive_acl_reset(abstract_acl, ae_requested_type);
345         if (entries == 0)
346                 return (ARCHIVE_OK);
347
348
349         switch (ae_requested_type) {
350         case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
351                 acl_type = ACL_TYPE_ACCESS;
352                 break;
353         case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
354                 acl_type = ACL_TYPE_DEFAULT;
355                 break;
356 #if ARCHIVE_ACL_FREEBSD_NFS4
357         case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
358                 acl_type = ACL_TYPE_NFS4;
359                 break;
360 #endif
361         default:
362                 errno = ENOENT;
363                 archive_set_error(a, errno, "Unsupported ACL type");
364                 return (ARCHIVE_FAILED);
365         }
366
367         acl = acl_init(entries);
368         if (acl == (acl_t)NULL) {
369                 archive_set_error(a, errno,
370                     "Failed to initialize ACL working storage");
371                 return (ARCHIVE_FAILED);
372         }
373
374         while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
375                    &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
376                 if (acl_create_entry(&acl, &acl_entry) != 0) {
377                         archive_set_error(a, errno,
378                             "Failed to create a new ACL entry");
379                         ret = ARCHIVE_FAILED;
380                         goto exit_free;
381                 }
382                 switch (ae_tag) {
383                 case ARCHIVE_ENTRY_ACL_USER:
384                         ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
385                         acl_set_tag_type(acl_entry, ACL_USER);
386                         acl_set_qualifier(acl_entry, &ae_uid);
387                         break;
388                 case ARCHIVE_ENTRY_ACL_GROUP:
389                         ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
390                         acl_set_tag_type(acl_entry, ACL_GROUP);
391                         acl_set_qualifier(acl_entry, &ae_gid);
392                         break;
393                 case ARCHIVE_ENTRY_ACL_USER_OBJ:
394                         acl_set_tag_type(acl_entry, ACL_USER_OBJ);
395                         break;
396                 case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
397                         acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
398                         break;
399                 case ARCHIVE_ENTRY_ACL_MASK:
400                         acl_set_tag_type(acl_entry, ACL_MASK);
401                         break;
402                 case ARCHIVE_ENTRY_ACL_OTHER:
403                         acl_set_tag_type(acl_entry, ACL_OTHER);
404                         break;
405 #if ARCHIVE_ACL_FREEBSD_NFS4
406                 case ARCHIVE_ENTRY_ACL_EVERYONE:
407                         acl_set_tag_type(acl_entry, ACL_EVERYONE);
408                         break;
409 #endif
410                 default:
411                         archive_set_error(a, ARCHIVE_ERRNO_MISC,
412                             "Unsupported ACL tag");
413                         ret = ARCHIVE_FAILED;
414                         goto exit_free;
415                 }
416
417 #if ARCHIVE_ACL_FREEBSD_NFS4
418                 r = 0;
419                 switch (ae_type) {
420                 case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
421                         r = acl_set_entry_type_np(acl_entry,
422                             ACL_ENTRY_TYPE_ALLOW);
423                         break;
424                 case ARCHIVE_ENTRY_ACL_TYPE_DENY:
425                         r = acl_set_entry_type_np(acl_entry,
426                             ACL_ENTRY_TYPE_DENY);
427                         break;
428                 case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
429                         r = acl_set_entry_type_np(acl_entry,
430                             ACL_ENTRY_TYPE_AUDIT);
431                         break;
432                 case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
433                         r = acl_set_entry_type_np(acl_entry,
434                             ACL_ENTRY_TYPE_ALARM);
435                         break;
436                 case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
437                 case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
438                         // These don't translate directly into the system ACL.
439                         break;
440                 default:
441                         archive_set_error(a, ARCHIVE_ERRNO_MISC,
442                             "Unsupported ACL entry type");
443                         ret = ARCHIVE_FAILED;
444                         goto exit_free;
445                 }
446
447                 if (r != 0) {
448                         archive_set_error(a, errno,
449                             "Failed to set ACL entry type");
450                         ret = ARCHIVE_FAILED;
451                         goto exit_free;
452                 }
453 #endif
454
455                 if (acl_get_permset(acl_entry, &acl_permset) != 0) {
456                         archive_set_error(a, errno,
457                             "Failed to get ACL permission set");
458                         ret = ARCHIVE_FAILED;
459                         goto exit_free;
460                 }
461                 if (acl_clear_perms(acl_permset) != 0) {
462                         archive_set_error(a, errno,
463                             "Failed to clear ACL permissions");
464                         ret = ARCHIVE_FAILED;
465                         goto exit_free;
466                 }
467 #if ARCHIVE_ACL_FREEBSD_NFS4
468                 if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
469                         perm_map_size = acl_nfs4_perm_map_size;
470                         perm_map = acl_nfs4_perm_map;
471                 } else {
472 #endif
473                         perm_map_size = acl_posix_perm_map_size;
474                         perm_map = acl_posix_perm_map;
475 #if ARCHIVE_ACL_FREEBSD_NFS4
476                 }
477 #endif
478
479                 for (i = 0; i < perm_map_size; ++i) {
480                         if (ae_permset & perm_map[i].a_perm) {
481                                 if (acl_add_perm(acl_permset,
482                                     perm_map[i].p_perm) != 0) {
483                                         archive_set_error(a, errno,
484                                             "Failed to add ACL permission");
485                                         ret = ARCHIVE_FAILED;
486                                         goto exit_free;
487                                 }
488                         }
489                 }
490
491 #if ARCHIVE_ACL_FREEBSD_NFS4
492                 if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
493                         /*
494                          * acl_get_flagset_np() fails with non-NFSv4 ACLs
495                          */
496                         if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
497                                 archive_set_error(a, errno,
498                                     "Failed to get flagset from an NFSv4 "
499                                     "ACL entry");
500                                 ret = ARCHIVE_FAILED;
501                                 goto exit_free;
502                         }
503                         if (acl_clear_flags_np(acl_flagset) != 0) {
504                                 archive_set_error(a, errno,
505                                     "Failed to clear flags from an NFSv4 "
506                                     "ACL flagset");
507                                 ret = ARCHIVE_FAILED;
508                                 goto exit_free;
509                         }
510                         for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
511                                 if (ae_permset & acl_nfs4_flag_map[i].a_perm) {
512                                         if (acl_add_flag_np(acl_flagset,
513                                             acl_nfs4_flag_map[i].p_perm) != 0) {
514                                                 archive_set_error(a, errno,
515                                                     "Failed to add flag to "
516                                                     "NFSv4 ACL flagset");
517                                                 ret = ARCHIVE_FAILED;
518                                                 goto exit_free;
519                                         }
520                                 }
521                         }
522                 }
523 #endif
524         }
525
526         /* Try restoring the ACL through 'fd' if we can. */
527         if (fd >= 0) {
528                 if (acl_set_fd_np(fd, acl, acl_type) == 0)
529                         ret = ARCHIVE_OK;
530                 else {
531                         if (errno == EOPNOTSUPP) {
532                                 /* Filesystem doesn't support ACLs */
533                                 ret = ARCHIVE_OK;
534                         } else {
535                                 archive_set_error(a, errno,
536                                     "Failed to set acl on fd: %s", tname);
537                                 ret = ARCHIVE_WARN;
538                         }
539                 }
540         }
541 #if HAVE_ACL_SET_LINK_NP
542         else if (acl_set_link_np(name, acl_type, acl) != 0)
543 #else
544         /* FreeBSD older than 8.0 */
545         else if (acl_set_file(name, acl_type, acl) != 0)
546 #endif
547         {
548                 if (errno == EOPNOTSUPP) {
549                         /* Filesystem doesn't support ACLs */
550                         ret = ARCHIVE_OK;
551                 } else {
552                         archive_set_error(a, errno, "Failed to set acl: %s",
553                             tname);
554                         ret = ARCHIVE_WARN;
555                 }
556         }
557 exit_free:
558         acl_free(acl);
559         return (ret);
560 }
561
562 int
563 archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
564     struct archive_entry *entry, int *fd)
565 {
566         const char      *accpath;
567         acl_t           acl;
568         int             r;
569
570         accpath = NULL;
571
572         if (*fd < 0) {
573                 accpath = archive_read_disk_entry_setup_path(a, entry, fd);
574                 if (accpath == NULL)
575                         return (ARCHIVE_WARN);
576         }
577
578         archive_entry_acl_clear(entry);
579
580         acl = NULL;
581
582 #if ARCHIVE_ACL_FREEBSD_NFS4
583         /* Try NFSv4 ACL first. */
584         if (*fd >= 0)
585                 acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4);
586         else if (!a->follow_symlinks)
587                 acl = acl_get_link_np(accpath, ACL_TYPE_NFS4);
588         else
589                 acl = acl_get_file(accpath, ACL_TYPE_NFS4);
590
591         /* Ignore "trivial" ACLs that just mirror the file mode. */
592         if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {
593                 acl_free(acl);
594                 acl = NULL;
595                 return (ARCHIVE_OK);
596         }
597
598         if (acl != NULL) {
599                 r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4);
600                 acl_free(acl);
601                 acl = NULL;
602
603                 if (r != ARCHIVE_OK) {
604                         archive_set_error(&a->archive, errno,
605                             "Couldn't translate NFSv4 ACLs");
606                 }
607
608                 return (r);
609         }
610 #endif
611
612         /* Retrieve access ACL from file. */
613         if (*fd >= 0)
614                 acl = acl_get_fd_np(*fd, ACL_TYPE_ACCESS);
615 #if HAVE_ACL_GET_LINK_NP
616         else if (!a->follow_symlinks)
617                 acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
618 #else
619         else if ((!a->follow_symlinks)
620             && (archive_entry_filetype(entry) == AE_IFLNK))
621                 /* We can't get the ACL of a symlink, so we assume it can't
622                    have one. */
623                 acl = NULL;
624 #endif
625         else
626                 acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
627
628 #if HAVE_ACL_IS_TRIVIAL_NP
629         /* Ignore "trivial" ACLs that just mirror the file mode. */
630         if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {
631                 acl_free(acl);
632                 acl = NULL;
633         }
634 #endif
635
636         if (acl != NULL) {
637                 r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
638                 acl_free(acl);
639                 acl = NULL;
640
641                 if (r != ARCHIVE_OK) {
642                         archive_set_error(&a->archive, errno,
643                             "Couldn't translate access ACLs");
644                         return (r);
645                 }
646         }
647
648         /* Only directories can have default ACLs. */
649         if (S_ISDIR(archive_entry_mode(entry))) {
650                 if (*fd >= 0)
651                         acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT);
652                 else
653                         acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
654                 if (acl != NULL) {
655                         r = translate_acl(a, entry, acl,
656                             ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
657                         acl_free(acl);
658                         if (r != ARCHIVE_OK) {
659                                 archive_set_error(&a->archive, errno,
660                                     "Couldn't translate default ACLs");
661                                 return (r);
662                         }
663                 }
664         }
665         return (ARCHIVE_OK);
666 }
667
668 int
669 archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
670     struct archive_acl *abstract_acl, __LA_MODE_T mode)
671 {
672         int             ret = ARCHIVE_OK;
673
674         (void)mode;     /* UNUSED */
675
676         if ((archive_acl_types(abstract_acl)
677             & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
678                 if ((archive_acl_types(abstract_acl)
679                     & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
680                         ret = set_acl(a, fd, name, abstract_acl,
681                             ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
682                         if (ret != ARCHIVE_OK)
683                                 return (ret);
684                 }
685                 if ((archive_acl_types(abstract_acl)
686                     & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
687                         ret = set_acl(a, fd, name, abstract_acl,
688                             ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
689
690                 /* Simultaneous POSIX.1e and NFSv4 is not supported */
691                 return (ret);
692         }
693 #if ARCHIVE_ACL_FREEBSD_NFS4
694         else if ((archive_acl_types(abstract_acl) &
695             ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
696                 ret = set_acl(a, fd, name, abstract_acl,
697                     ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
698         }
699 #endif
700         return (ret);
701 }
702 #endif  /* ARCHIVE_ACL_FREEBSD */