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