(root)/
tar-1.35/
gnu/
set-permissions.c
       1  /* Set permissions of a file.  -*- coding: utf-8 -*-
       2  
       3     Copyright (C) 2002-2003, 2005-2023 Free Software Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.
      17  
      18     Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible.  */
      19  
      20  #include <config.h>
      21  
      22  #include "acl.h"
      23  
      24  #include "acl-internal.h"
      25  
      26  #if USE_ACL
      27  # if ! defined HAVE_ACL_FROM_MODE && defined HAVE_ACL_FROM_TEXT /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
      28  #  if HAVE_ACL_GET_FILE && !HAVE_ACL_TYPE_EXTENDED
      29  
      30  static acl_t
      31  acl_from_mode (mode_t mode)
      32  {
      33  #  if HAVE_ACL_FREE_TEXT /* Tru64 */
      34    char acl_text[] = "u::---,g::---,o::---,";
      35  #  else /* FreeBSD, IRIX, Cygwin >= 2.5 */
      36    char acl_text[] = "u::---,g::---,o::---";
      37  #  endif
      38  
      39    if (mode & S_IRUSR) acl_text[ 3] = 'r';
      40    if (mode & S_IWUSR) acl_text[ 4] = 'w';
      41    if (mode & S_IXUSR) acl_text[ 5] = 'x';
      42    if (mode & S_IRGRP) acl_text[10] = 'r';
      43    if (mode & S_IWGRP) acl_text[11] = 'w';
      44    if (mode & S_IXGRP) acl_text[12] = 'x';
      45    if (mode & S_IROTH) acl_text[17] = 'r';
      46    if (mode & S_IWOTH) acl_text[18] = 'w';
      47    if (mode & S_IXOTH) acl_text[19] = 'x';
      48  
      49    return acl_from_text (acl_text);
      50  }
      51  #  endif
      52  # endif
      53  
      54  # if HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
      55  static int
      56  set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
      57  {
      58  #  ifdef ACE_GETACL
      59    /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
      60       file systems (whereas the other ones are used in UFS file systems).  */
      61  
      62    /* The flags in the ace_t structure changed in a binary incompatible way
      63       when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
      64       How to distinguish the two conventions at runtime?
      65       We fetch the existing ACL.  In the old convention, usually three ACEs have
      66       a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
      67       In the new convention, these values are not used.  */
      68    int convention;
      69  
      70    {
      71      /* Initially, try to read the entries into a stack-allocated buffer.
      72         Use malloc if it does not fit.  */
      73      enum
      74        {
      75          alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
      76          alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
      77        };
      78      ace_t buf[alloc_init];
      79      size_t alloc = alloc_init;
      80      ace_t *entries = buf;
      81      ace_t *malloced = NULL;
      82      int count;
      83  
      84      for (;;)
      85        {
      86          count = (desc != -1
      87                   ? facl (desc, ACE_GETACL, alloc, entries)
      88                   : acl (name, ACE_GETACL, alloc, entries));
      89          if (count < 0 && errno == ENOSPC)
      90            {
      91              /* Increase the size of the buffer.  */
      92              free (malloced);
      93              if (alloc > alloc_max / 2)
      94                {
      95                  errno = ENOMEM;
      96                  return -1;
      97                }
      98              alloc = 2 * alloc; /* <= alloc_max */
      99              entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
     100              if (entries == NULL)
     101                {
     102                  errno = ENOMEM;
     103                  return -1;
     104                }
     105              continue;
     106            }
     107          break;
     108        }
     109  
     110      if (count <= 0)
     111        convention = -1;
     112      else
     113        {
     114          int i;
     115  
     116          convention = 0;
     117          for (i = 0; i < count; i++)
     118            if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
     119              {
     120                convention = 1;
     121                break;
     122              }
     123        }
     124      free (malloced);
     125    }
     126  
     127    if (convention >= 0)
     128      {
     129        ace_t entries[6];
     130        int count;
     131        int ret;
     132  
     133        if (convention)
     134          {
     135            /* Running on Solaris 10.  */
     136            entries[0].a_type = OLD_ALLOW;
     137            entries[0].a_flags = OLD_ACE_OWNER;
     138            entries[0].a_who = 0; /* irrelevant */
     139            entries[0].a_access_mask = (mode >> 6) & 7;
     140            entries[1].a_type = OLD_ALLOW;
     141            entries[1].a_flags = OLD_ACE_GROUP;
     142            entries[1].a_who = 0; /* irrelevant */
     143            entries[1].a_access_mask = (mode >> 3) & 7;
     144            entries[2].a_type = OLD_ALLOW;
     145            entries[2].a_flags = OLD_ACE_OTHER;
     146            entries[2].a_who = 0;
     147            entries[2].a_access_mask = mode & 7;
     148            count = 3;
     149          }
     150        else
     151          {
     152            /* Running on Solaris 10 (newer version) or Solaris 11.
     153               The details here were found through "/bin/ls -lvd somefiles".  */
     154            entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
     155            entries[0].a_flags = NEW_ACE_OWNER;
     156            entries[0].a_who = 0; /* irrelevant */
     157            entries[0].a_access_mask = 0;
     158            entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
     159            entries[1].a_flags = NEW_ACE_OWNER;
     160            entries[1].a_who = 0; /* irrelevant */
     161            entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
     162                                       | NEW_ACE_WRITE_ATTRIBUTES
     163                                       | NEW_ACE_WRITE_ACL
     164                                       | NEW_ACE_WRITE_OWNER;
     165            if (mode & 0400)
     166              entries[1].a_access_mask |= NEW_ACE_READ_DATA;
     167            else
     168              entries[0].a_access_mask |= NEW_ACE_READ_DATA;
     169            if (mode & 0200)
     170              entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
     171            else
     172              entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
     173            if (mode & 0100)
     174              entries[1].a_access_mask |= NEW_ACE_EXECUTE;
     175            else
     176              entries[0].a_access_mask |= NEW_ACE_EXECUTE;
     177            entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
     178            entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
     179            entries[2].a_who = 0; /* irrelevant */
     180            entries[2].a_access_mask = 0;
     181            entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
     182            entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
     183            entries[3].a_who = 0; /* irrelevant */
     184            entries[3].a_access_mask = 0;
     185            if (mode & 0040)
     186              entries[3].a_access_mask |= NEW_ACE_READ_DATA;
     187            else
     188              entries[2].a_access_mask |= NEW_ACE_READ_DATA;
     189            if (mode & 0020)
     190              entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
     191            else
     192              entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
     193            if (mode & 0010)
     194              entries[3].a_access_mask |= NEW_ACE_EXECUTE;
     195            else
     196              entries[2].a_access_mask |= NEW_ACE_EXECUTE;
     197            entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
     198            entries[4].a_flags = NEW_ACE_EVERYONE;
     199            entries[4].a_who = 0;
     200            entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
     201                                       | NEW_ACE_WRITE_ATTRIBUTES
     202                                       | NEW_ACE_WRITE_ACL
     203                                       | NEW_ACE_WRITE_OWNER;
     204            entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
     205            entries[5].a_flags = NEW_ACE_EVERYONE;
     206            entries[5].a_who = 0;
     207            entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
     208                                       | NEW_ACE_READ_ATTRIBUTES
     209                                       | NEW_ACE_READ_ACL
     210                                       | NEW_ACE_SYNCHRONIZE;
     211            if (mode & 0004)
     212              entries[5].a_access_mask |= NEW_ACE_READ_DATA;
     213            else
     214              entries[4].a_access_mask |= NEW_ACE_READ_DATA;
     215            if (mode & 0002)
     216              entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
     217            else
     218              entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
     219            if (mode & 0001)
     220              entries[5].a_access_mask |= NEW_ACE_EXECUTE;
     221            else
     222              entries[4].a_access_mask |= NEW_ACE_EXECUTE;
     223            count = 6;
     224          }
     225        if (desc != -1)
     226          ret = facl (desc, ACE_SETACL, count, entries);
     227        else
     228          ret = acl (name, ACE_SETACL, count, entries);
     229        if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
     230          {
     231            if (errno == ENOSYS)
     232              {
     233                *must_chmod = true;
     234                return 0;
     235              }
     236            return -1;
     237          }
     238        if (ret == 0)
     239          return 0;
     240      }
     241  #  endif
     242  
     243    {
     244      aclent_t entries[3];
     245      int ret;
     246  
     247      entries[0].a_type = USER_OBJ;
     248      entries[0].a_id = 0; /* irrelevant */
     249      entries[0].a_perm = (mode >> 6) & 7;
     250      entries[1].a_type = GROUP_OBJ;
     251      entries[1].a_id = 0; /* irrelevant */
     252      entries[1].a_perm = (mode >> 3) & 7;
     253      entries[2].a_type = OTHER_OBJ;
     254      entries[2].a_id = 0;
     255      entries[2].a_perm = mode & 7;
     256  
     257      if (desc != -1)
     258        ret = facl (desc, SETACL,
     259                    sizeof (entries) / sizeof (aclent_t), entries);
     260      else
     261        ret = acl (name, SETACL,
     262                   sizeof (entries) / sizeof (aclent_t), entries);
     263      if (ret < 0)
     264        {
     265          if (errno == ENOSYS || errno == EOPNOTSUPP)
     266            {
     267              *must_chmod = true;
     268              return 0;
     269            }
     270          return -1;
     271        }
     272      return 0;
     273    }
     274  }
     275  
     276  # elif HAVE_GETACL /* HP-UX */
     277  static int
     278  context_acl_from_mode (struct permission_context *ctx, const char *name, int desc)
     279  {
     280    struct stat statbuf;
     281    int ret;
     282  
     283    if (desc != -1)
     284      ret = fstat (desc, &statbuf);
     285    else
     286      ret = stat (name, &statbuf);
     287    if (ret < 0)
     288      return -1;
     289  
     290    ctx->entries[0].uid = statbuf.st_uid;
     291    ctx->entries[0].gid = ACL_NSGROUP;
     292    ctx->entries[0].mode = (ctx->mode >> 6) & 7;
     293    ctx->entries[1].uid = ACL_NSUSER;
     294    ctx->entries[1].gid = statbuf.st_gid;
     295    ctx->entries[1].mode = (ctx->mode >> 3) & 7;
     296    ctx->entries[2].uid = ACL_NSUSER;
     297    ctx->entries[2].gid = ACL_NSGROUP;
     298    ctx->entries[2].mode = ctx->mode & 7;
     299    ctx->count = 3;
     300    return 0;
     301  }
     302  
     303  #  if HAVE_ACLV_H /* HP-UX >= 11.11 */
     304  static int
     305  context_aclv_from_mode (struct permission_context *ctx)
     306  {
     307    int ret;
     308  
     309    ctx->aclv_entries[0].a_type = USER_OBJ;
     310    ctx->aclv_entries[0].a_id = 0; /* irrelevant */
     311    ctx->aclv_entries[0].a_perm = (ctx->mode >> 6) & 7;
     312    ctx->aclv_entries[1].a_type = GROUP_OBJ;
     313    ctx->aclv_entries[1].a_id = 0; /* irrelevant */
     314    ctx->aclv_entries[1].a_perm = (ctx->mode >> 3) & 7;
     315    ctx->aclv_entries[2].a_type = CLASS_OBJ;
     316    ctx->aclv_entries[2].a_id = 0;
     317    ctx->aclv_entries[2].a_perm = (ctx->mode >> 3) & 7;
     318    ctx->aclv_entries[3].a_type = OTHER_OBJ;
     319    ctx->aclv_entries[3].a_id = 0;
     320    ctx->aclv_entries[3].a_perm = ctx->mode & 7;
     321    ctx->aclv_count = 4;
     322  
     323    ret = aclsort (ctx->aclv_count, 1, ctx->aclv_entries);
     324    if (ret > 0)
     325      abort ();
     326    return ret;
     327  }
     328  #  endif
     329  
     330  # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
     331  static int
     332  set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
     333  {
     334    acl_type_list_t types;
     335    size_t types_size = sizeof (types);
     336    acl_type_t type;
     337  
     338    if (aclx_gettypes (name, &types, &types_size) < 0
     339        || types.num_entries == 0)
     340      {
     341        *must_chmod = true;
     342        return 0;
     343      }
     344  
     345    /* XXX Do we need to clear all types of ACLs for the given file, or is it
     346       sufficient to clear the first one?  */
     347    type = types.entries[0];
     348    if (type.u64 == ACL_AIXC)
     349      {
     350        union { struct acl a; char room[128]; } u;
     351        int ret;
     352  
     353        u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
     354        u.a.acl_mode = mode & ~(S_IXACL | 0777);
     355        u.a.u_access = (mode >> 6) & 7;
     356        u.a.g_access = (mode >> 3) & 7;
     357        u.a.o_access = mode & 7;
     358  
     359        if (desc != -1)
     360          ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
     361                           type, &u.a, u.a.acl_len, mode);
     362        else
     363          ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
     364                          type, &u.a, u.a.acl_len, mode);
     365        if (!(ret < 0 && errno == ENOSYS))
     366          return ret;
     367      }
     368    else if (type.u64 == ACL_NFS4)
     369      {
     370        union { nfs4_acl_int_t a; char room[128]; } u;
     371        nfs4_ace_int_t *ace;
     372        int ret;
     373  
     374        u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
     375        u.a.aclEntryN = 0;
     376        ace = &u.a.aclEntry[0];
     377        {
     378          ace->flags = ACE4_ID_SPECIAL;
     379          ace->aceWho.special_whoid = ACE4_WHO_OWNER;
     380          ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
     381          ace->aceFlags = 0;
     382          ace->aceMask =
     383            (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
     384            | (mode & 0200
     385               ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
     386                 | ACE4_ADD_SUBDIRECTORY
     387               : 0)
     388            | (mode & 0100 ? ACE4_EXECUTE : 0);
     389          ace->aceWhoString[0] = '\0';
     390          ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
     391          ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
     392          u.a.aclEntryN++;
     393        }
     394        {
     395          ace->flags = ACE4_ID_SPECIAL;
     396          ace->aceWho.special_whoid = ACE4_WHO_GROUP;
     397          ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
     398          ace->aceFlags = 0;
     399          ace->aceMask =
     400            (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
     401            | (mode & 0020
     402               ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
     403                 | ACE4_ADD_SUBDIRECTORY
     404               : 0)
     405            | (mode & 0010 ? ACE4_EXECUTE : 0);
     406          ace->aceWhoString[0] = '\0';
     407          ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
     408          ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
     409          u.a.aclEntryN++;
     410        }
     411        {
     412          ace->flags = ACE4_ID_SPECIAL;
     413          ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
     414          ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
     415          ace->aceFlags = 0;
     416          ace->aceMask =
     417            (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
     418            | (mode & 0002
     419               ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
     420                 | ACE4_ADD_SUBDIRECTORY
     421               : 0)
     422            | (mode & 0001 ? ACE4_EXECUTE : 0);
     423          ace->aceWhoString[0] = '\0';
     424          ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
     425          ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
     426          u.a.aclEntryN++;
     427        }
     428        u.a.aclLength = (char *) ace - (char *) &u.a;
     429  
     430        if (desc != -1)
     431          ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
     432                           type, &u.a, u.a.aclLength, mode);
     433        else
     434          ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
     435                          type, &u.a, u.a.aclLength, mode);
     436        if (!(ret < 0 && errno == ENOSYS))
     437          return ret;
     438      }
     439  
     440    *must_chmod = true;
     441    return 0;
     442  }
     443  
     444  # elif HAVE_STATACL /* older AIX */
     445  static int
     446  context_acl_from_mode (struct permission_context *ctx)
     447  {
     448    ctx->u.a.acl_len = (char *) &ctx->u.a.acl_ext[0] - (char *) &ctx->u.a; /* no entries */
     449    ctx->u.a.acl_mode = ctx->mode & ~(S_IXACL | 0777);
     450    ctx->u.a.u_access = (ctx->mode >> 6) & 7;
     451    ctx->u.a.g_access = (ctx->mode >> 3) & 7;
     452    ctx->u.a.o_access = ctx->mode & 7;
     453    ctx->have_u = true;
     454    return 0;
     455  }
     456  
     457  # elif HAVE_ACLSORT /* NonStop Kernel */
     458  static int
     459  context_acl_from_mode (struct permission_context *ctx)
     460  {
     461    int ret;
     462  
     463    ctx->entries[0].a_type = USER_OBJ;
     464    ctx->entries[0].a_id = 0; /* irrelevant */
     465    ctx->entries[0].a_perm = (ctx->mode >> 6) & 7;
     466    ctx->entries[1].a_type = GROUP_OBJ;
     467    ctx->entries[1].a_id = 0; /* irrelevant */
     468    ctx->entries[1].a_perm = (ctx->mode >> 3) & 7;
     469    ctx->entries[2].a_type = CLASS_OBJ;
     470    ctx->entries[2].a_id = 0;
     471    ctx->entries[2].a_perm = (ctx->mode >> 3) & 7;
     472    ctx->entries[3].a_type = OTHER_OBJ;
     473    ctx->entries[3].a_id = 0;
     474    ctx->entries[3].a_perm = ctx->mode & 7;
     475    ctx->count = 4;
     476  
     477    ret = aclsort (ctx->count, 1, entries);
     478    if (ret > 0)
     479      abort ();
     480    return ret;
     481  }
     482  # endif
     483  
     484  static int
     485  set_acls (struct permission_context *ctx, const char *name, int desc,
     486            int from_mode, bool *must_chmod, bool *acls_set)
     487  {
     488    int ret = 0;
     489  
     490  # if HAVE_ACL_GET_FILE
     491    /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
     492    /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
     493  #  if !HAVE_ACL_TYPE_EXTENDED
     494    /* Linux, FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
     495  
     496  #   ifndef HAVE_ACL_FROM_TEXT
     497  #    error Must have acl_from_text (see POSIX 1003.1e draft 17).
     498  #   endif
     499  #   ifndef HAVE_ACL_DELETE_DEF_FILE
     500  #    error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
     501  #   endif
     502  
     503    if (! ctx->acls_not_supported)
     504      {
     505        if (ret == 0 && from_mode)
     506          {
     507            if (ctx->acl)
     508              acl_free (ctx->acl);
     509            ctx->acl = acl_from_mode (ctx->mode);
     510            if (ctx->acl == NULL)
     511              ret = -1;
     512          }
     513  
     514        if (ret == 0 && ctx->acl)
     515          {
     516            if (HAVE_ACL_SET_FD && desc != -1)
     517              ret = acl_set_fd (desc, ctx->acl);
     518            else
     519              ret = acl_set_file (name, ACL_TYPE_ACCESS, ctx->acl);
     520            if (ret != 0)
     521              {
     522                if (! acl_errno_valid (errno))
     523                  {
     524                    ctx->acls_not_supported = true;
     525                    if (from_mode || acl_access_nontrivial (ctx->acl) == 0)
     526                      ret = 0;
     527                  }
     528              }
     529            else
     530              {
     531                *acls_set = true;
     532                if (S_ISDIR(ctx->mode))
     533                  {
     534                    if (! from_mode && ctx->default_acl &&
     535                        acl_default_nontrivial (ctx->default_acl))
     536                      ret = acl_set_file (name, ACL_TYPE_DEFAULT,
     537                                          ctx->default_acl);
     538                    else
     539                      ret = acl_delete_def_file (name);
     540                  }
     541              }
     542          }
     543      }
     544  
     545  #   if HAVE_ACL_TYPE_NFS4  /* FreeBSD */
     546  
     547    /* File systems either support POSIX ACLs (for example, ufs) or NFS4 ACLs
     548       (for example, zfs). */
     549  
     550    /* TODO: Implement setting ACLs once get_permissions() reads them. */
     551  
     552  #   endif
     553  
     554  #  else /* HAVE_ACL_TYPE_EXTENDED */
     555    /* Mac OS X */
     556  
     557    /* On Mac OS X,  acl_get_file (name, ACL_TYPE_ACCESS)
     558       and           acl_get_file (name, ACL_TYPE_DEFAULT)
     559       always return NULL / EINVAL.  You have to use
     560                     acl_get_file (name, ACL_TYPE_EXTENDED)
     561       or            acl_get_fd (open (name, ...))
     562       to retrieve an ACL.
     563       On the other hand,
     564                     acl_set_file (name, ACL_TYPE_ACCESS, acl)
     565       and           acl_set_file (name, ACL_TYPE_DEFAULT, acl)
     566       have the same effect as
     567                     acl_set_file (name, ACL_TYPE_EXTENDED, acl):
     568       Each of these calls sets the file's ACL.  */
     569  
     570    if (ctx->acl == NULL)
     571      {
     572        acl_t acl;
     573  
     574        /* Remove ACLs if the file has ACLs.  */
     575        if (HAVE_ACL_GET_FD && desc != -1)
     576          acl = acl_get_fd (desc);
     577        else
     578          acl = acl_get_file (name, ACL_TYPE_EXTENDED);
     579        if (acl)
     580          {
     581            acl_free (acl);
     582  
     583            acl = acl_init (0);
     584            if (acl)
     585              {
     586                if (HAVE_ACL_SET_FD && desc != -1)
     587                  ret = acl_set_fd (desc, acl);
     588                else
     589                  ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
     590                acl_free (acl);
     591              }
     592            else
     593              ret = -1;
     594          }
     595      }
     596    else
     597      {
     598        if (HAVE_ACL_SET_FD && desc != -1)
     599          ret = acl_set_fd (desc, ctx->acl);
     600        else
     601          ret = acl_set_file (name, ACL_TYPE_EXTENDED, ctx->acl);
     602        if (ret != 0)
     603          {
     604            if (! acl_errno_valid (errno)
     605                && ! acl_extended_nontrivial (ctx->acl))
     606              ret = 0;
     607          }
     608      }
     609    *acls_set = true;
     610  
     611  #  endif
     612  
     613  # elif defined GETACL /* Solaris, Cygwin, not HP-UX */
     614  
     615    /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
     616       of Unixware.  The acl() call returns the access and default ACL both
     617       at once.  */
     618  
     619    /* If both ace_entries and entries are available, try SETACL before
     620       ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
     621       can.  */
     622  
     623    if (from_mode)
     624      return set_acls_from_mode (name, desc, ctx->mode, must_chmod);
     625  
     626    if (ret == 0 && ctx->count)
     627      {
     628        if (desc != -1)
     629          ret = facl (desc, SETACL, ctx->count, ctx->entries);
     630        else
     631          ret = acl (name, SETACL, ctx->count, ctx->entries);
     632        if (ret < 0)
     633          {
     634            if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
     635                && acl_nontrivial (ctx->count, ctx->entries) == 0)
     636              ret = 0;
     637          }
     638        else
     639          *acls_set = true;
     640      }
     641  
     642  #  ifdef ACE_GETACL
     643    if (ret == 0 && ctx->ace_count)
     644      {
     645        if (desc != -1)
     646          ret = facl (desc, ACE_SETACL, ctx->ace_count, ctx->ace_entries);
     647        else
     648          ret = acl (name, ACE_SETACL, ctx->ace_count, ctx->ace_entries);
     649        if (ret < 0)
     650          {
     651            if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
     652                && acl_ace_nontrivial (ctx->ace_count, ctx->ace_entries) == 0)
     653              ret = 0;
     654          }
     655        else
     656          *acls_set = true;
     657      }
     658  #  endif
     659  
     660  # elif HAVE_GETACL /* HP-UX */
     661  
     662    if (from_mode)
     663      ret = context_acl_from_mode (ctx, name, desc);
     664  
     665    if (ret == 0 && ctx->count > 0)
     666      {
     667        if (desc != -1)
     668          ret = fsetacl (desc, ctx->count, ctx->entries);
     669        else
     670          ret = setacl (name, ctx->count, ctx->entries);
     671        if (ret < 0)
     672          {
     673            if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
     674                && (from_mode || !acl_nontrivial (ctx->count, ctx->entries)))
     675              ret = 0;
     676          }
     677        else
     678          *acls_set = true;
     679      }
     680  
     681  #  if HAVE_ACLV_H
     682    if (from_mode)
     683      ret = context_aclv_from_mode (ctx);
     684  
     685    if (ret == 0 && ctx->aclv_count > 0)
     686      {
     687        ret = acl ((char *) name, ACL_SET, ctx->aclv_count, ctx->aclv_entries);
     688        if (ret < 0)
     689          {
     690            if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
     691                && (from_mode || !aclv_nontrivial (ctx->aclv_count, ctx->aclv_entries)))
     692              ret = 0;
     693          }
     694        else
     695          *acls_set = true;
     696      }
     697  #  endif
     698  
     699  # elif HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */
     700  
     701    /* TODO: Implement setting ACLs once get_permissions() reads them. */
     702  
     703    if (from_mode)
     704      ret = set_acls_from_mode (name, desc, mode, must_chmod);
     705  
     706  # elif HAVE_STATACL /* older AIX */
     707  
     708    if (from_mode)
     709      ret = context_acl_from_mode (ctx);
     710  
     711    if (ret == 0 && ctx->have_u)
     712      {
     713        if (desc != -1)
     714          ret = fchacl (desc, &ctx->u.a, ctx->u.a.acl_len);
     715        else
     716          ret = chacl ((char *) name, &ctx->u.a, ctx->u.a.acl_len);
     717        if (ret < 0)
     718          {
     719            if (errno == ENOSYS && from_mode)
     720              ret = 0;
     721          }
     722        else
     723          *acls_set = true;
     724      }
     725  
     726  # elif HAVE_ACLSORT /* NonStop Kernel */
     727  
     728    if (from_mode)
     729      ret = context_acl_from_mode (ctx);
     730  
     731    if (ret == 0 && ctx->count)
     732      {
     733        ret = acl ((char *) name, ACL_SET, ctx->count, ctx->entries);
     734        if (ret != 0)
     735          {
     736            if (!acl_nontrivial (ctx->count, ctx->entries))
     737              ret = 0;
     738          }
     739        else
     740          *acls_set = true;
     741      }
     742  
     743  # else  /* No ACLs */
     744  
     745    /* Nothing to do. */
     746  
     747  # endif
     748  
     749    return ret;
     750  }
     751  #endif
     752  
     753  /* If DESC is a valid file descriptor use fchmod to change the
     754     file's mode to MODE on systems that have fchmod. On systems
     755     that don't have fchmod and if DESC is invalid, use chmod on
     756     NAME instead.
     757     Return 0 if successful.  Return -1 and set errno upon failure.  */
     758  
     759  int
     760  chmod_or_fchmod (const char *name, int desc, mode_t mode)
     761  {
     762    if (HAVE_FCHMOD && desc != -1)
     763      return fchmod (desc, mode);
     764    else
     765      return chmod (name, mode);
     766  }
     767  
     768  /* Set the permissions in CTX on a file. If DESC is a valid file descriptor,
     769     use file descriptor operations, else use filename based operations on NAME.
     770     If access control lists are not available, fchmod the target file to the
     771     mode in CTX.  Also sets the non-permission bits of the destination file
     772     (S_ISUID, S_ISGID, S_ISVTX) to those from the mode in CTX if any are set.
     773     Return 0 if successful.  Return -1 and set errno upon failure.  */
     774  
     775  int
     776  set_permissions (struct permission_context *ctx, const char *name, int desc)
     777  {
     778    _GL_UNUSED bool acls_set = false;
     779    bool early_chmod;
     780    bool must_chmod = false;
     781    int ret = 0;
     782  
     783  #if USE_ACL
     784  # if HAVE_STATACL
     785    /* older AIX */
     786    /* There is no need to call chmod_or_fchmod, since the mode
     787       bits S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL.  */
     788  
     789    early_chmod = false;
     790  # else
     791    /* All other platforms */
     792    /* On Cygwin, it is necessary to call chmod before acl, because
     793       chmod can change the contents of the ACL (in ways that don't
     794       change the allowed accesses, but still visible).  */
     795  
     796    early_chmod = (! MODE_INSIDE_ACL || (ctx->mode & (S_ISUID | S_ISGID | S_ISVTX)));
     797  # endif
     798  #else
     799    /* No ACLs */
     800  
     801    early_chmod = true;
     802  #endif
     803  
     804    if (early_chmod)
     805      {
     806        ret = chmod_or_fchmod (name, desc, ctx->mode);
     807        if (ret != 0)
     808          return -1;
     809      }
     810  
     811  #if USE_ACL
     812    ret = set_acls (ctx, name, desc, false, &must_chmod, &acls_set);
     813    if (! acls_set)
     814      {
     815        int saved_errno = ret ? errno : 0;
     816  
     817        /* If we can't set an acl which we expect to be able to set, try setting
     818           the permissions to ctx->mode. Due to possible inherited permissions,
     819           we cannot simply chmod.  */
     820  
     821        ret = set_acls (ctx, name, desc, true, &must_chmod, &acls_set);
     822        if (! acls_set)
     823          must_chmod = true;
     824  
     825        if (saved_errno)
     826          {
     827            errno = saved_errno;
     828            ret = -1;
     829          }
     830      }
     831  #endif
     832  
     833    if (must_chmod && ! early_chmod)
     834      {
     835        int saved_errno = ret ? errno : 0;
     836  
     837        ret = chmod_or_fchmod (name, desc, ctx->mode);
     838  
     839        if (saved_errno)
     840          {
     841            errno = saved_errno;
     842            ret = -1;
     843          }
     844      }
     845  
     846    return ret;
     847  }