static int retrieve_acl( const char *path_p, acl_type_t type, const struct stat *st, acl_t *old_acl, acl_t *acl) { if (*acl) return 0; *acl = NULL; if (type == ACL_TYPE_ACCESS || S_ISDIR(st->st_mode)) { *old_acl = acl_get_file(path_p, type); if (*old_acl == NULL && (errno == ENOSYS || errno == ENOTSUP)) { if (type == ACL_TYPE_DEFAULT) *old_acl = acl_init(0); else *old_acl = acl_from_mode(st->st_mode); } } else *old_acl = acl_init(0); if (*old_acl == NULL) return -1; *acl = acl_dup(*old_acl); if (*acl == NULL) return -1; return 0; }
/* 23.4.15 */ acl_t acl_get_fd(int fd) { const size_t size_guess = acl_ea_size(16); char *ext_acl_p = alloca(size_guess); int retval; if (!ext_acl_p) return NULL; retval = fgetxattr(fd, ACL_EA_ACCESS, ext_acl_p, size_guess); if (retval == -1 && errno == ERANGE) { retval = fgetxattr(fd, ACL_EA_ACCESS, NULL, 0); if (retval > 0) { ext_acl_p = alloca(retval); if (!ext_acl_p) return NULL; retval = fgetxattr(fd, ACL_EA_ACCESS, ext_acl_p,retval); } } if (retval > 0) { acl_t acl = __acl_from_xattr(ext_acl_p, retval); return acl; } else if (retval == 0 || errno == ENOATTR || errno == ENODATA) { struct stat st; if (fstat(fd, &st) == 0) return acl_from_mode(st.st_mode); else return NULL; } else return NULL; }
/* * Create an ACL from the file permission bits * of the file PATH_P. */ static acl_t acl_get_file_mode(const char *path_p) { struct stat st; if (stat(path_p, &st) != 0) return NULL; return acl_from_mode(st.st_mode); }
/* Set the access control list of path to the permissions defined by mode. */ static int set_acl (char const *path, mode_t mode, struct error_context *ctx) { int ret = 0; #if defined(HAVE_ACL_FROM_MODE) && defined(HAVE_ACL_SET_FILE) /* POSIX 1003.1e draft 17 (abandoned) specific version. */ acl_t acl = acl_from_mode (mode); if (!acl) { error (ctx, ""); return -1; } if (acl_set_file (path, ACL_TYPE_ACCESS, acl) != 0) { ret = -1; if (errno == ENOTSUP || errno == ENOSYS) { (void) acl_free (acl); goto chmod_only; } else { const char *qpath = quote (ctx, path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } } (void) acl_free (acl); if (ret == 0 && S_ISDIR (mode)) { # if defined(HAVE_ACL_DELETE_DEF_FILE) ret = acl_delete_def_file (path); # else acl = acl_init (0); ret = acl_set_file (path, ACL_TYPE_DEFAULT, acl); (void) acl_free (acl); # endif if (ret != 0) { const char *qpath = quote (ctx, path); error (ctx, _( "setting permissions for %s"), qpath); quote_free (ctx, qpath); } } return ret; #endif chmod_only: ret = chmod (path, mode); if (ret != 0) { const char *qpath = quote (ctx, path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } return ret; }
int getacl(const char *pathp, aclent_t *aclpbuf) { acl_t acl = NULL, default_acl = NULL; struct stat st; if (stat(pathp, &st) != 0) { return -1; } acl = acl_get_file(pathp, ACL_TYPE_ACCESS); if(acl == NULL && (errno == ENOSYS || errno == ENOTSUP)) { acl = acl_from_mode(st.st_mode); if (acl == NULL) { return -1; } } if (S_ISDIR(st.st_mode)) { default_acl = acl_get_file(pathp, ACL_TYPE_DEFAULT); if ((default_acl != NULL) && (acl_entries(default_acl) == 0)) { acl_free(default_acl); default_acl = NULL; } } acl_entry_t acl_entry; int ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); int i = 0; while(ret > 0) { aclpbuf[i++] = getentry(acl_entry, st, 0); ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); } acl_free(acl); if((default_acl != NULL) && (ret != -1)) { ret = acl_get_entry(default_acl, ACL_FIRST_ENTRY, &acl_entry); while(ret > 0) { aclpbuf[i++] = getentry(acl_entry, st, ACL_DEFAULT); ret = acl_get_entry(default_acl, ACL_NEXT_ENTRY, &acl_entry); } acl_free(default_acl); } return i; }
/* Set the access control list of path to the permissions defined by mode. */ static int set_acl_fd (char const *path, int fd, mode_t mode, struct error_context *ctx) { int ret = 0; #if defined(HAVE_ACL_FROM_MODE) && defined(HAVE_ACL_SET_FD) /* POSIX 1003.1e draft 17 (abandoned) specific version. */ acl_t acl = acl_from_mode (mode); if (!acl) { error (ctx, ""); return -1; } if (acl_set_fd (fd, acl) != 0) { ret = -1; if (errno == ENOTSUP || errno == ENOSYS) { (void) acl_free (acl); goto chmod_only; } else { const char *qpath = quote (ctx, path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } } (void) acl_free (acl); return ret; #endif chmod_only: ret = fchmod (fd, mode); if (ret != 0) { const char *qpath = quote (ctx, path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } return ret; }
int qset_acl (char const *name, int desc, mode_t mode) { #if USE_ACL # if HAVE_ACL_GET_FILE /* POSIX 1003.1e draft 17 (abandoned) specific version. */ /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */ # if !HAVE_ACL_TYPE_EXTENDED /* Linux, FreeBSD, IRIX, Tru64 */ /* We must also have acl_from_text and acl_delete_def_file. (acl_delete_def_file could be emulated with acl_init followed by acl_set_file, but acl_set_file with an empty acl is unspecified.) */ # ifndef HAVE_ACL_FROM_TEXT # error Must have acl_from_text (see POSIX 1003.1e draft 17). # endif # ifndef HAVE_ACL_DELETE_DEF_FILE # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17). # endif acl_t acl; int ret; if (HAVE_ACL_FROM_MODE) /* Linux */ { acl = acl_from_mode (mode); if (!acl) return -1; } else /* FreeBSD, IRIX, Tru64 */ { /* If we were to create the ACL using the functions acl_init(), acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(), acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we would need to create a qualifier. I don't know how to do this. So create it using acl_from_text(). */ # if HAVE_ACL_FREE_TEXT /* Tru64 */ char acl_text[] = "u::---,g::---,o::---,"; # else /* FreeBSD, IRIX */ char acl_text[] = "u::---,g::---,o::---"; # endif if (mode & S_IRUSR) acl_text[ 3] = 'r'; if (mode & S_IWUSR) acl_text[ 4] = 'w'; if (mode & S_IXUSR) acl_text[ 5] = 'x'; if (mode & S_IRGRP) acl_text[10] = 'r'; if (mode & S_IWGRP) acl_text[11] = 'w'; if (mode & S_IXGRP) acl_text[12] = 'x'; if (mode & S_IROTH) acl_text[17] = 'r'; if (mode & S_IWOTH) acl_text[18] = 'w'; if (mode & S_IXOTH) acl_text[19] = 'x'; acl = acl_from_text (acl_text); if (!acl) return -1; } if (HAVE_ACL_SET_FD && desc != -1) ret = acl_set_fd (desc, acl); else ret = acl_set_file (name, ACL_TYPE_ACCESS, acl); if (ret != 0) { int saved_errno = errno; acl_free (acl); if (ACL_NOT_WELL_SUPPORTED (errno)) return chmod_or_fchmod (name, desc, mode); else { errno = saved_errno; return -1; } } else acl_free (acl); if (S_ISDIR (mode) && acl_delete_def_file (name)) return -1; if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX))) { /* We did not call chmod so far, and either the mode and the ACL are separate or special bits are to be set which don't fit into ACLs. */ return chmod_or_fchmod (name, desc, mode); } return 0; # else /* HAVE_ACL_TYPE_EXTENDED */ /* MacOS X */ /* On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS) and acl_get_file (name, ACL_TYPE_DEFAULT) always return NULL / EINVAL. You have to use acl_get_file (name, ACL_TYPE_EXTENDED) or acl_get_fd (open (name, ...)) to retrieve an ACL. On the other hand, acl_set_file (name, ACL_TYPE_ACCESS, acl) and acl_set_file (name, ACL_TYPE_DEFAULT, acl) have the same effect as acl_set_file (name, ACL_TYPE_EXTENDED, acl): Each of these calls sets the file's ACL. */ acl_t acl; int ret; /* Remove the ACL if the file has ACLs. */ if (HAVE_ACL_GET_FD && desc != -1) acl = acl_get_fd (desc); else acl = acl_get_file (name, ACL_TYPE_EXTENDED); if (acl) { acl_free (acl); acl = acl_init (0); if (acl) { if (HAVE_ACL_SET_FD && desc != -1) ret = acl_set_fd (desc, acl); else ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl); if (ret != 0) { int saved_errno = errno; acl_free (acl); if (ACL_NOT_WELL_SUPPORTED (saved_errno)) return chmod_or_fchmod (name, desc, mode); else { errno = saved_errno; return -1; } } acl_free (acl); } } /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */ return chmod_or_fchmod (name, desc, mode); # endif # elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */ int done_setacl = 0; # ifdef ACE_GETACL /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4 file systems (whereas the other ones are used in UFS file systems). */ /* The flags in the ace_t structure changed in a binary incompatible way when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15. How to distinguish the two conventions at runtime? We fetch the existing ACL. In the old convention, usually three ACEs have a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400. In the new convention, these values are not used. */ int convention; { int count; ace_t *entries; for (;;) { if (desc != -1) count = facl (desc, ACE_GETACLCNT, 0, NULL); else count = acl (name, ACE_GETACLCNT, 0, NULL); if (count <= 0) { convention = -1; break; } entries = (ace_t *) malloc (count * sizeof (ace_t)); if (entries == NULL) { errno = ENOMEM; return -1; } if ((desc != -1 ? facl (desc, ACE_GETACL, count, entries) : acl (name, ACE_GETACL, count, entries)) == count) { int i; convention = 0; for (i = 0; i < count; i++) if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER)) { convention = 1; break; } free (entries); break; } /* Huh? The number of ACL entries changed since the last call. Repeat. */ free (entries); } } if (convention >= 0) { ace_t entries[6]; int count; int ret; if (convention) { /* Running on Solaris 10. */ entries[0].a_type = OLD_ALLOW; entries[0].a_flags = OLD_ACE_OWNER; entries[0].a_who = 0; /* irrelevant */ entries[0].a_access_mask = (mode >> 6) & 7; entries[1].a_type = OLD_ALLOW; entries[1].a_flags = OLD_ACE_GROUP; entries[1].a_who = 0; /* irrelevant */ entries[1].a_access_mask = (mode >> 3) & 7; entries[2].a_type = OLD_ALLOW; entries[2].a_flags = OLD_ACE_OTHER; entries[2].a_who = 0; entries[2].a_access_mask = mode & 7; count = 3; } else {
int set_acl (char const *name, int desc, mode_t mode) { #if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE /* POSIX 1003.1e draft 17 (abandoned) specific version. */ /* We must also have have_acl_from_text and acl_delete_def_file. (acl_delete_def_file could be emulated with acl_init followed by acl_set_file, but acl_set_file with an empty acl is unspecified.) */ # ifndef HAVE_ACL_FROM_TEXT # error Must have acl_from_text (see POSIX 1003.1e draft 17). # endif # ifndef HAVE_ACL_DELETE_DEF_FILE # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17). # endif acl_t acl; int ret; if (HAVE_ACL_FROM_MODE) { acl = acl_from_mode (mode); if (!acl) { error (0, errno, "%s", quote (name)); return -1; } } else { char acl_text[] = "u::---,g::---,o::---"; if (mode & S_IRUSR) acl_text[ 3] = 'r'; if (mode & S_IWUSR) acl_text[ 4] = 'w'; if (mode & S_IXUSR) acl_text[ 5] = 'x'; if (mode & S_IRGRP) acl_text[10] = 'r'; if (mode & S_IWGRP) acl_text[11] = 'w'; if (mode & S_IXGRP) acl_text[12] = 'x'; if (mode & S_IROTH) acl_text[17] = 'r'; if (mode & S_IWOTH) acl_text[18] = 'w'; if (mode & S_IXOTH) acl_text[19] = 'x'; acl = acl_from_text (acl_text); if (!acl) { error (0, errno, "%s", quote (name)); return -1; } } if (HAVE_ACL_SET_FD && desc != -1) ret = acl_set_fd (desc, acl); else ret = acl_set_file (name, ACL_TYPE_ACCESS, acl); if (ret != 0) { int saved_errno = errno; acl_free (acl); if (ACL_NOT_WELL_SUPPORTED (errno)) { if (chmod_or_fchmod (name, desc, mode) != 0) saved_errno = errno; else return 0; } error (0, saved_errno, _("setting permissions for %s"), quote (name)); return -1; } else acl_free (acl); if (S_ISDIR (mode) && acl_delete_def_file (name)) { error (0, errno, _("setting permissions for %s"), quote (name)); return -1; } if (mode & (S_ISUID | S_ISGID | S_ISVTX)) { /* We did not call chmod so far, so the special bits have not yet been set. */ if (chmod_or_fchmod (name, desc, mode)) { error (0, errno, _("preserving permissions for %s"), quote (name)); return -1; } } return 0; #else int ret = chmod_or_fchmod (name, desc, mode); if (ret) error (0, errno, _("setting permissions for %s"), quote (name)); return ret; #endif }
int add_base_acls_if_needed(acl_t *acl_p, const char *path) { acl_entry_t i; int r; bool have_user_obj = false, have_group_obj = false, have_other = false; struct stat st; _cleanup_(acl_freep) acl_t basic = NULL; assert(acl_p); for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i); r > 0; r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) { acl_tag_t tag; if (acl_get_tag_type(i, &tag) < 0) return -errno; if (tag == ACL_USER_OBJ) have_user_obj = true; else if (tag == ACL_GROUP_OBJ) have_group_obj = true; else if (tag == ACL_OTHER) have_other = true; if (have_user_obj && have_group_obj && have_other) return 0; } if (r < 0) return -errno; r = stat(path, &st); if (r < 0) return -errno; basic = acl_from_mode(st.st_mode); if (!basic) return -errno; for (r = acl_get_entry(basic, ACL_FIRST_ENTRY, &i); r > 0; r = acl_get_entry(basic, ACL_NEXT_ENTRY, &i)) { acl_tag_t tag; acl_entry_t dst; if (acl_get_tag_type(i, &tag) < 0) return -errno; if ((tag == ACL_USER_OBJ && have_user_obj) || (tag == ACL_GROUP_OBJ && have_group_obj) || (tag == ACL_OTHER && have_other)) continue; r = acl_create_entry(acl_p, &dst); if (r < 0) return -errno; r = acl_copy_entry(dst, i); if (r < 0) return -errno; } if (r < 0) return -errno; return 0; }