| /*- |
| * Copyright (c) 2003-2009 Tim Kientzle |
| * Copyright (c) 2010-2012 Michihiro NAKAJIMA |
| * Copyright (c) 2016-2017 Martin Matuska |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "archive_platform.h" |
| |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| #ifdef HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| #if HAVE_ACL_LIBACL_H && HAVE_LIBACL |
| #include <acl/libacl.h> |
| #endif |
| #ifdef HAVE_SYS_ACL_H |
| #include <sys/acl.h> |
| #endif |
| |
| #include "archive_entry.h" |
| #include "archive_private.h" |
| #include "archive_read_disk_private.h" |
| #include "archive_acl_maps.h" |
| |
| #if HAVE_LIBACL |
| #include <acl/libacl.h> |
| #endif |
| |
| /* |
| * Translate POSIX.1e ACLs into libarchive internal structure |
| */ |
| static int |
| translate_acl(struct archive_read_disk *a, |
| struct archive_entry *entry, acl_t acl, int default_entry_acl_type) |
| { |
| acl_tag_t acl_tag; |
| acl_entry_t acl_entry; |
| acl_permset_t acl_permset; |
| int i, entry_acl_type; |
| int r, s, ae_id, ae_tag, ae_perm; |
| void *q; |
| const char *ae_name; |
| |
| s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); |
| if (s == -1) { |
| archive_set_error(&a->archive, errno, |
| "Failed to get first ACL entry"); |
| return (ARCHIVE_WARN); |
| } |
| |
| while (s == 1) { |
| ae_id = -1; |
| ae_name = NULL; |
| ae_perm = 0; |
| |
| if (acl_get_tag_type(acl_entry, &acl_tag) != 0) { |
| archive_set_error(&a->archive, errno, |
| "Failed to get ACL tag type"); |
| return (ARCHIVE_WARN); |
| } |
| switch (acl_tag) { |
| case ACL_USER: |
| q = acl_get_qualifier(acl_entry); |
| if (q != NULL) { |
| ae_id = (int)*(uid_t *)q; |
| acl_free(q); |
| ae_name = archive_read_disk_uname(&a->archive, |
| ae_id); |
| } |
| ae_tag = ARCHIVE_ENTRY_ACL_USER; |
| break; |
| case ACL_GROUP: |
| q = acl_get_qualifier(acl_entry); |
| if (q != NULL) { |
| ae_id = (int)*(gid_t *)q; |
| acl_free(q); |
| ae_name = archive_read_disk_gname(&a->archive, |
| ae_id); |
| } |
| ae_tag = ARCHIVE_ENTRY_ACL_GROUP; |
| break; |
| case ACL_MASK: |
| ae_tag = ARCHIVE_ENTRY_ACL_MASK; |
| break; |
| case ACL_USER_OBJ: |
| ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; |
| break; |
| case ACL_GROUP_OBJ: |
| ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; |
| break; |
| case ACL_OTHER: |
| ae_tag = ARCHIVE_ENTRY_ACL_OTHER; |
| break; |
| default: |
| /* Skip types that libarchive can't support. */ |
| s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); |
| continue; |
| } |
| |
| // XXX acl_type maps to allow/deny/audit/YYYY bits |
| entry_acl_type = default_entry_acl_type; |
| |
| if (acl_get_permset(acl_entry, &acl_permset) != 0) { |
| archive_set_error(&a->archive, errno, |
| "Failed to get ACL permission set"); |
| return (ARCHIVE_WARN); |
| } |
| |
| for (i = 0; i < acl_posix_perm_map_size; ++i) { |
| r = acl_get_perm(acl_permset, |
| acl_posix_perm_map[i].p_perm); |
| if (r == -1) { |
| archive_set_error(&a->archive, errno, |
| "Failed to check permission in an ACL " |
| "permission set"); |
| return (ARCHIVE_WARN); |
| } else if (r) |
| ae_perm |= acl_posix_perm_map[i].a_perm; |
| } |
| |
| archive_entry_acl_add_entry(entry, entry_acl_type, |
| ae_perm, ae_tag, |
| ae_id, ae_name); |
| |
| s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); |
| if (s == -1) { |
| archive_set_error(&a->archive, errno, |
| "Failed to get next ACL entry"); |
| return (ARCHIVE_WARN); |
| } |
| } |
| return (ARCHIVE_OK); |
| } |
| int |
| archive_read_disk_entry_setup_acls(struct archive_read_disk *a, |
| struct archive_entry *entry, int *fd) |
| { |
| const char *accpath; |
| acl_t acl; |
| int r; |
| |
| accpath = NULL; |
| |
| /* For default ACLs we need reachable accpath */ |
| if (*fd < 0 || S_ISDIR(archive_entry_mode(entry))) |
| { |
| accpath = archive_entry_sourcepath(entry); |
| if (accpath == NULL || (a->tree != NULL && |
| a->tree_enter_working_dir(a->tree) != 0)) |
| accpath = archive_entry_pathname(entry); |
| if (accpath == NULL) { |
| archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, |
| "Couldn't determine file path to read ACLs"); |
| return (ARCHIVE_WARN); |
| } |
| if (a->tree != NULL && *fd < 0 && (a->follow_symlinks || |
| archive_entry_filetype(entry) != AE_IFLNK)) { |
| *fd = a->open_on_current_dir(a->tree, |
| accpath, O_RDONLY | O_NONBLOCK); |
| } |
| } |
| |
| archive_entry_acl_clear(entry); |
| |
| acl = NULL; |
| |
| /* Retrieve access ACL from file. */ |
| if (*fd >= 0) |
| acl = acl_get_fd(*fd); |
| else if ((!a->follow_symlinks) |
| && (archive_entry_filetype(entry) == AE_IFLNK)) |
| /* We can't get the ACL of a symlink, so we assume it can't |
| have one. */ |
| acl = NULL; |
| else |
| acl = acl_get_file(accpath, ACL_TYPE_ACCESS); |
| |
| if (acl != NULL) { |
| r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); |
| acl_free(acl); |
| acl = NULL; |
| |
| if (r != ARCHIVE_OK) { |
| archive_set_error(&a->archive, errno, |
| "Couldn't translate access ACLs"); |
| return (r); |
| } |
| } |
| |
| /* Only directories can have default ACLs. */ |
| if (S_ISDIR(archive_entry_mode(entry))) { |
| acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); |
| if (acl != NULL) { |
| r = translate_acl(a, entry, acl, |
| ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); |
| acl_free(acl); |
| if (r != ARCHIVE_OK) { |
| archive_set_error(&a->archive, errno, |
| "Couldn't translate default ACLs"); |
| return (r); |
| } |
| } |
| } |
| return (ARCHIVE_OK); |
| } |