/* Set permissions of a file. -*- coding: utf-8 -*- Copyright (C) 2002-2003, 2005-2023 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */ #include #include "acl.h" #include "acl-internal.h" #if USE_ACL # if ! defined HAVE_ACL_FROM_MODE && defined HAVE_ACL_FROM_TEXT /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */ # if HAVE_ACL_GET_FILE && !HAVE_ACL_TYPE_EXTENDED static acl_t acl_from_mode (mode_t mode) { # if HAVE_ACL_FREE_TEXT /* Tru64 */ char acl_text[] = "u::---,g::---,o::---,"; # else /* FreeBSD, IRIX, Cygwin >= 2.5 */ 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'; return acl_from_text (acl_text); } # endif # endif # if HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */ static int set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod) { # 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 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; { /* Initially, try to read the entries into a stack-allocated buffer. Use malloc if it does not fit. */ enum { alloc_init = 4000 / sizeof (ace_t), /* >= 3 */ alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t)) }; ace_t buf[alloc_init]; size_t alloc = alloc_init; ace_t *entries = buf; ace_t *malloced = NULL; int count; for (;;) { count = (desc != -1 ? facl (desc, ACE_GETACL, alloc, entries) : acl (name, ACE_GETACL, alloc, entries)); if (count < 0 && errno == ENOSPC) { /* Increase the size of the buffer. */ free (malloced); if (alloc > alloc_max / 2) { errno = ENOMEM; return -1; } alloc = 2 * alloc; /* <= alloc_max */ entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t)); if (entries == NULL) { errno = ENOMEM; return -1; } continue; } break; } if (count <= 0) convention = -1; else { 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 (malloced); } 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 { /* Running on Solaris 10 (newer version) or Solaris 11. The details here were found through "/bin/ls -lvd somefiles". */ entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; entries[0].a_flags = NEW_ACE_OWNER; entries[0].a_who = 0; /* irrelevant */ entries[0].a_access_mask = 0; entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; entries[1].a_flags = NEW_ACE_OWNER; entries[1].a_who = 0; /* irrelevant */ entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS | NEW_ACE_WRITE_ATTRIBUTES | NEW_ACE_WRITE_ACL | NEW_ACE_WRITE_OWNER; if (mode & 0400) entries[1].a_access_mask |= NEW_ACE_READ_DATA; else entries[0].a_access_mask |= NEW_ACE_READ_DATA; if (mode & 0200) entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; else entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; if (mode & 0100) entries[1].a_access_mask |= NEW_ACE_EXECUTE; else entries[0].a_access_mask |= NEW_ACE_EXECUTE; entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP; entries[2].a_who = 0; /* irrelevant */ entries[2].a_access_mask = 0; entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP; entries[3].a_who = 0; /* irrelevant */ entries[3].a_access_mask = 0; if (mode & 0040) entries[3].a_access_mask |= NEW_ACE_READ_DATA; else entries[2].a_access_mask |= NEW_ACE_READ_DATA; if (mode & 0020) entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; else entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; if (mode & 0010) entries[3].a_access_mask |= NEW_ACE_EXECUTE; else entries[2].a_access_mask |= NEW_ACE_EXECUTE; entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE; entries[4].a_flags = NEW_ACE_EVERYONE; entries[4].a_who = 0; entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS | NEW_ACE_WRITE_ATTRIBUTES | NEW_ACE_WRITE_ACL | NEW_ACE_WRITE_OWNER; entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE; entries[5].a_flags = NEW_ACE_EVERYONE; entries[5].a_who = 0; entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS | NEW_ACE_READ_ATTRIBUTES | NEW_ACE_READ_ACL | NEW_ACE_SYNCHRONIZE; if (mode & 0004) entries[5].a_access_mask |= NEW_ACE_READ_DATA; else entries[4].a_access_mask |= NEW_ACE_READ_DATA; if (mode & 0002) entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; else entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA; if (mode & 0001) entries[5].a_access_mask |= NEW_ACE_EXECUTE; else entries[4].a_access_mask |= NEW_ACE_EXECUTE; count = 6; } if (desc != -1) ret = facl (desc, ACE_SETACL, count, entries); else ret = acl (name, ACE_SETACL, count, entries); if (ret < 0 && errno != EINVAL && errno != ENOTSUP) { if (errno == ENOSYS) { *must_chmod = true; return 0; } return -1; } if (ret == 0) return 0; } # endif { aclent_t entries[3]; int ret; entries[0].a_type = USER_OBJ; entries[0].a_id = 0; /* irrelevant */ entries[0].a_perm = (mode >> 6) & 7; entries[1].a_type = GROUP_OBJ; entries[1].a_id = 0; /* irrelevant */ entries[1].a_perm = (mode >> 3) & 7; entries[2].a_type = OTHER_OBJ; entries[2].a_id = 0; entries[2].a_perm = mode & 7; if (desc != -1) ret = facl (desc, SETACL, sizeof (entries) / sizeof (aclent_t), entries); else ret = acl (name, SETACL, sizeof (entries) / sizeof (aclent_t), entries); if (ret < 0) { if (errno == ENOSYS || errno == EOPNOTSUPP) { *must_chmod = true; return 0; } return -1; } return 0; } } # elif HAVE_GETACL /* HP-UX */ static int context_acl_from_mode (struct permission_context *ctx, const char *name, int desc) { struct stat statbuf; int ret; if (desc != -1) ret = fstat (desc, &statbuf); else ret = stat (name, &statbuf); if (ret < 0) return -1; ctx->entries[0].uid = statbuf.st_uid; ctx->entries[0].gid = ACL_NSGROUP; ctx->entries[0].mode = (ctx->mode >> 6) & 7; ctx->entries[1].uid = ACL_NSUSER; ctx->entries[1].gid = statbuf.st_gid; ctx->entries[1].mode = (ctx->mode >> 3) & 7; ctx->entries[2].uid = ACL_NSUSER; ctx->entries[2].gid = ACL_NSGROUP; ctx->entries[2].mode = ctx->mode & 7; ctx->count = 3; return 0; } # if HAVE_ACLV_H /* HP-UX >= 11.11 */ static int context_aclv_from_mode (struct permission_context *ctx) { int ret; ctx->aclv_entries[0].a_type = USER_OBJ; ctx->aclv_entries[0].a_id = 0; /* irrelevant */ ctx->aclv_entries[0].a_perm = (ctx->mode >> 6) & 7; ctx->aclv_entries[1].a_type = GROUP_OBJ; ctx->aclv_entries[1].a_id = 0; /* irrelevant */ ctx->aclv_entries[1].a_perm = (ctx->mode >> 3) & 7; ctx->aclv_entries[2].a_type = CLASS_OBJ; ctx->aclv_entries[2].a_id = 0; ctx->aclv_entries[2].a_perm = (ctx->mode >> 3) & 7; ctx->aclv_entries[3].a_type = OTHER_OBJ; ctx->aclv_entries[3].a_id = 0; ctx->aclv_entries[3].a_perm = ctx->mode & 7; ctx->aclv_count = 4; ret = aclsort (ctx->aclv_count, 1, ctx->aclv_entries); if (ret > 0) abort (); return ret; } # endif # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */ static int set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod) { acl_type_list_t types; size_t types_size = sizeof (types); acl_type_t type; if (aclx_gettypes (name, &types, &types_size) < 0 || types.num_entries == 0) { *must_chmod = true; return 0; } /* XXX Do we need to clear all types of ACLs for the given file, or is it sufficient to clear the first one? */ type = types.entries[0]; if (type.u64 == ACL_AIXC) { union { struct acl a; char room[128]; } u; int ret; u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */ u.a.acl_mode = mode & ~(S_IXACL | 0777); u.a.u_access = (mode >> 6) & 7; u.a.g_access = (mode >> 3) & 7; u.a.o_access = mode & 7; if (desc != -1) ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS, type, &u.a, u.a.acl_len, mode); else ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS, type, &u.a, u.a.acl_len, mode); if (!(ret < 0 && errno == ENOSYS)) return ret; } else if (type.u64 == ACL_NFS4) { union { nfs4_acl_int_t a; char room[128]; } u; nfs4_ace_int_t *ace; int ret; u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION; u.a.aclEntryN = 0; ace = &u.a.aclEntry[0]; { ace->flags = ACE4_ID_SPECIAL; ace->aceWho.special_whoid = ACE4_WHO_OWNER; ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; ace->aceFlags = 0; ace->aceMask = (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) | (mode & 0200 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY : 0) | (mode & 0100 ? ACE4_EXECUTE : 0); ace->aceWhoString[0] = '\0'; ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; u.a.aclEntryN++; } { ace->flags = ACE4_ID_SPECIAL; ace->aceWho.special_whoid = ACE4_WHO_GROUP; ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; ace->aceFlags = 0; ace->aceMask = (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) | (mode & 0020 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY : 0) | (mode & 0010 ? ACE4_EXECUTE : 0); ace->aceWhoString[0] = '\0'; ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; u.a.aclEntryN++; } { ace->flags = ACE4_ID_SPECIAL; ace->aceWho.special_whoid = ACE4_WHO_EVERYONE; ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE; ace->aceFlags = 0; ace->aceMask = (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0) | (mode & 0002 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY : 0) | (mode & 0001 ? ACE4_EXECUTE : 0); ace->aceWhoString[0] = '\0'; ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace; ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4]; u.a.aclEntryN++; } u.a.aclLength = (char *) ace - (char *) &u.a; if (desc != -1) ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS, type, &u.a, u.a.aclLength, mode); else ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS, type, &u.a, u.a.aclLength, mode); if (!(ret < 0 && errno == ENOSYS)) return ret; } *must_chmod = true; return 0; } # elif HAVE_STATACL /* older AIX */ static int context_acl_from_mode (struct permission_context *ctx) { ctx->u.a.acl_len = (char *) &ctx->u.a.acl_ext[0] - (char *) &ctx->u.a; /* no entries */ ctx->u.a.acl_mode = ctx->mode & ~(S_IXACL | 0777); ctx->u.a.u_access = (ctx->mode >> 6) & 7; ctx->u.a.g_access = (ctx->mode >> 3) & 7; ctx->u.a.o_access = ctx->mode & 7; ctx->have_u = true; return 0; } # elif HAVE_ACLSORT /* NonStop Kernel */ static int context_acl_from_mode (struct permission_context *ctx) { int ret; ctx->entries[0].a_type = USER_OBJ; ctx->entries[0].a_id = 0; /* irrelevant */ ctx->entries[0].a_perm = (ctx->mode >> 6) & 7; ctx->entries[1].a_type = GROUP_OBJ; ctx->entries[1].a_id = 0; /* irrelevant */ ctx->entries[1].a_perm = (ctx->mode >> 3) & 7; ctx->entries[2].a_type = CLASS_OBJ; ctx->entries[2].a_id = 0; ctx->entries[2].a_perm = (ctx->mode >> 3) & 7; ctx->entries[3].a_type = OTHER_OBJ; ctx->entries[3].a_id = 0; ctx->entries[3].a_perm = ctx->mode & 7; ctx->count = 4; ret = aclsort (ctx->count, 1, entries); if (ret > 0) abort (); return ret; } # endif static int set_acls (struct permission_context *ctx, const char *name, int desc, int from_mode, bool *must_chmod, bool *acls_set) { int ret = 0; # if HAVE_ACL_GET_FILE /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */ /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */ # if !HAVE_ACL_TYPE_EXTENDED /* Linux, FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */ # 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 if (! ctx->acls_not_supported) { if (ret == 0 && from_mode) { if (ctx->acl) acl_free (ctx->acl); ctx->acl = acl_from_mode (ctx->mode); if (ctx->acl == NULL) ret = -1; } if (ret == 0 && ctx->acl) { if (HAVE_ACL_SET_FD && desc != -1) ret = acl_set_fd (desc, ctx->acl); else ret = acl_set_file (name, ACL_TYPE_ACCESS, ctx->acl); if (ret != 0) { if (! acl_errno_valid (errno)) { ctx->acls_not_supported = true; if (from_mode || acl_access_nontrivial (ctx->acl) == 0) ret = 0; } } else { *acls_set = true; if (S_ISDIR(ctx->mode)) { if (! from_mode && ctx->default_acl && acl_default_nontrivial (ctx->default_acl)) ret = acl_set_file (name, ACL_TYPE_DEFAULT, ctx->default_acl); else ret = acl_delete_def_file (name); } } } } # if HAVE_ACL_TYPE_NFS4 /* FreeBSD */ /* File systems either support POSIX ACLs (for example, ufs) or NFS4 ACLs (for example, zfs). */ /* TODO: Implement setting ACLs once get_permissions() reads them. */ # endif # else /* HAVE_ACL_TYPE_EXTENDED */ /* Mac OS X */ /* On Mac OS 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. */ if (ctx->acl == NULL) { acl_t acl; /* Remove ACLs 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); acl_free (acl); } else ret = -1; } } else { if (HAVE_ACL_SET_FD && desc != -1) ret = acl_set_fd (desc, ctx->acl); else ret = acl_set_file (name, ACL_TYPE_EXTENDED, ctx->acl); if (ret != 0) { if (! acl_errno_valid (errno) && ! acl_extended_nontrivial (ctx->acl)) ret = 0; } } *acls_set = true; # endif # elif defined GETACL /* Solaris, Cygwin, not HP-UX */ /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions of Unixware. The acl() call returns the access and default ACL both at once. */ /* If both ace_entries and entries are available, try SETACL before ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL can. */ if (from_mode) return set_acls_from_mode (name, desc, ctx->mode, must_chmod); if (ret == 0 && ctx->count) { if (desc != -1) ret = facl (desc, SETACL, ctx->count, ctx->entries); else ret = acl (name, SETACL, ctx->count, ctx->entries); if (ret < 0) { if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) && acl_nontrivial (ctx->count, ctx->entries) == 0) ret = 0; } else *acls_set = true; } # ifdef ACE_GETACL if (ret == 0 && ctx->ace_count) { if (desc != -1) ret = facl (desc, ACE_SETACL, ctx->ace_count, ctx->ace_entries); else ret = acl (name, ACE_SETACL, ctx->ace_count, ctx->ace_entries); if (ret < 0) { if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP) && acl_ace_nontrivial (ctx->ace_count, ctx->ace_entries) == 0) ret = 0; } else *acls_set = true; } # endif # elif HAVE_GETACL /* HP-UX */ if (from_mode) ret = context_acl_from_mode (ctx, name, desc); if (ret == 0 && ctx->count > 0) { if (desc != -1) ret = fsetacl (desc, ctx->count, ctx->entries); else ret = setacl (name, ctx->count, ctx->entries); if (ret < 0) { if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP) && (from_mode || !acl_nontrivial (ctx->count, ctx->entries))) ret = 0; } else *acls_set = true; } # if HAVE_ACLV_H if (from_mode) ret = context_aclv_from_mode (ctx); if (ret == 0 && ctx->aclv_count > 0) { ret = acl ((char *) name, ACL_SET, ctx->aclv_count, ctx->aclv_entries); if (ret < 0) { if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL) && (from_mode || !aclv_nontrivial (ctx->aclv_count, ctx->aclv_entries))) ret = 0; } else *acls_set = true; } # endif # elif HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */ /* TODO: Implement setting ACLs once get_permissions() reads them. */ if (from_mode) ret = set_acls_from_mode (name, desc, mode, must_chmod); # elif HAVE_STATACL /* older AIX */ if (from_mode) ret = context_acl_from_mode (ctx); if (ret == 0 && ctx->have_u) { if (desc != -1) ret = fchacl (desc, &ctx->u.a, ctx->u.a.acl_len); else ret = chacl ((char *) name, &ctx->u.a, ctx->u.a.acl_len); if (ret < 0) { if (errno == ENOSYS && from_mode) ret = 0; } else *acls_set = true; } # elif HAVE_ACLSORT /* NonStop Kernel */ if (from_mode) ret = context_acl_from_mode (ctx); if (ret == 0 && ctx->count) { ret = acl ((char *) name, ACL_SET, ctx->count, ctx->entries); if (ret != 0) { if (!acl_nontrivial (ctx->count, ctx->entries)) ret = 0; } else *acls_set = true; } # else /* No ACLs */ /* Nothing to do. */ # endif return ret; } #endif /* If DESC is a valid file descriptor use fchmod to change the file's mode to MODE on systems that have fchmod. On systems that don't have fchmod and if DESC is invalid, use chmod on NAME instead. Return 0 if successful. Return -1 and set errno upon failure. */ int chmod_or_fchmod (const char *name, int desc, mode_t mode) { if (HAVE_FCHMOD && desc != -1) return fchmod (desc, mode); else return chmod (name, mode); } /* Set the permissions in CTX on a file. If DESC is a valid file descriptor, use file descriptor operations, else use filename based operations on NAME. If access control lists are not available, fchmod the target file to the mode in CTX. Also sets the non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX) to those from the mode in CTX if any are set. Return 0 if successful. Return -1 and set errno upon failure. */ int set_permissions (struct permission_context *ctx, const char *name, int desc) { _GL_UNUSED bool acls_set = false; bool early_chmod; bool must_chmod = false; int ret = 0; #if USE_ACL # if HAVE_STATACL /* older AIX */ /* There is no need to call chmod_or_fchmod, since the mode bits S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */ early_chmod = false; # else /* All other platforms */ /* On Cygwin, it is necessary to call chmod before acl, because chmod can change the contents of the ACL (in ways that don't change the allowed accesses, but still visible). */ early_chmod = (! MODE_INSIDE_ACL || (ctx->mode & (S_ISUID | S_ISGID | S_ISVTX))); # endif #else /* No ACLs */ early_chmod = true; #endif if (early_chmod) { ret = chmod_or_fchmod (name, desc, ctx->mode); if (ret != 0) return -1; } #if USE_ACL ret = set_acls (ctx, name, desc, false, &must_chmod, &acls_set); if (! acls_set) { int saved_errno = ret ? errno : 0; /* If we can't set an acl which we expect to be able to set, try setting the permissions to ctx->mode. Due to possible inherited permissions, we cannot simply chmod. */ ret = set_acls (ctx, name, desc, true, &must_chmod, &acls_set); if (! acls_set) must_chmod = true; if (saved_errno) { errno = saved_errno; ret = -1; } } #endif if (must_chmod && ! early_chmod) { int saved_errno = ret ? errno : 0; ret = chmod_or_fchmod (name, desc, ctx->mode); if (saved_errno) { errno = saved_errno; ret = -1; } } return ret; }