(root)/
tar-1.35/
gnu/
get-permissions.c
       1  /* Get 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 <string.h>
      23  #include "acl.h"
      24  
      25  #include "acl-internal.h"
      26  
      27  /* Read the permissions of a file into CTX. If DESC is a valid file descriptor,
      28     use file descriptor operations, else use filename based operations on NAME.
      29     MODE is the file mode obtained from a previous stat call.
      30     Return 0 if successful.  Return -1 and set errno upon failure.  */
      31  
      32  int
      33  get_permissions (const char *name, int desc, mode_t mode,
      34                   struct permission_context *ctx)
      35  {
      36    memset (ctx, 0, sizeof *ctx);
      37    ctx->mode = mode;
      38  
      39  #if USE_ACL && HAVE_ACL_GET_FILE
      40    /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
      41    /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
      42  # if !HAVE_ACL_TYPE_EXTENDED
      43    /* Linux, FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
      44  
      45    if (HAVE_ACL_GET_FD && desc != -1)
      46      ctx->acl = acl_get_fd (desc);
      47    else
      48      ctx->acl = acl_get_file (name, ACL_TYPE_ACCESS);
      49    if (ctx->acl == NULL)
      50      return acl_errno_valid (errno) ? -1 : 0;
      51  
      52    /* With POSIX ACLs, a file cannot have "no" acl; a file without
      53       extended permissions has a "minimal" acl which is equivalent to the
      54       file mode.  */
      55  
      56    if (S_ISDIR (mode))
      57      {
      58        ctx->default_acl = acl_get_file (name, ACL_TYPE_DEFAULT);
      59        if (ctx->default_acl == NULL)
      60          return -1;
      61      }
      62  
      63  #  if HAVE_ACL_TYPE_NFS4  /* FreeBSD */
      64  
      65    /* TODO (see set_permissions). */
      66  
      67  #  endif
      68  
      69  # else /* HAVE_ACL_TYPE_EXTENDED */
      70    /* Mac OS X */
      71  
      72    /* On Mac OS X,  acl_get_file (name, ACL_TYPE_ACCESS)
      73       and           acl_get_file (name, ACL_TYPE_DEFAULT)
      74       always return NULL / EINVAL.  You have to use
      75                     acl_get_file (name, ACL_TYPE_EXTENDED)
      76       or            acl_get_fd (open (name, ...))
      77       to retrieve an ACL.
      78       On the other hand,
      79                     acl_set_file (name, ACL_TYPE_ACCESS, acl)
      80       and           acl_set_file (name, ACL_TYPE_DEFAULT, acl)
      81       have the same effect as
      82                     acl_set_file (name, ACL_TYPE_EXTENDED, acl):
      83       Each of these calls sets the file's ACL.  */
      84  
      85    if (HAVE_ACL_GET_FD && desc != -1)
      86      ctx->acl = acl_get_fd (desc);
      87    else
      88      ctx->acl = acl_get_file (name, ACL_TYPE_EXTENDED);
      89    if (ctx->acl == NULL)
      90      return acl_errno_valid (errno) ? -1 : 0;
      91  
      92  # endif
      93  
      94  #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
      95  
      96    /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
      97       of Unixware.  The acl() call returns the access and default ACL both
      98       at once.  */
      99  # ifdef ACE_GETACL
     100    /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
     101       file systems (whereas the other ones are used in UFS file systems).
     102       There is an API
     103         pathconf (name, _PC_ACL_ENABLED)
     104         fpathconf (desc, _PC_ACL_ENABLED)
     105       that allows us to determine which of the two kinds of ACLs is supported
     106       for the given file.  But some file systems may implement this call
     107       incorrectly, so better not use it.
     108       When fetching the source ACL, we simply fetch both ACL types.
     109       When setting the destination ACL, we try either ACL types, assuming
     110       that the kernel will translate the ACL from one form to the other.
     111       (See in <https://docs.oracle.com/cd/E86824_01/html/E54765/acl-2.html>
     112       the description of ENOTSUP.)  */
     113    for (;;)
     114      {
     115        int ret;
     116  
     117        if (desc != -1)
     118          ret = facl (desc, ACE_GETACLCNT, 0, NULL);
     119        else
     120          ret = acl (name, ACE_GETACLCNT, 0, NULL);
     121        if (ret < 0)
     122          {
     123            if (errno == ENOSYS || errno == EINVAL)
     124              ret = 0;
     125            else
     126              return -1;
     127          }
     128        ctx->ace_count = ret;
     129  
     130        if (ctx->ace_count == 0)
     131          break;
     132  
     133        ctx->ace_entries = (ace_t *) malloc (ctx->ace_count * sizeof (ace_t));
     134        if (ctx->ace_entries == NULL)
     135          {
     136            errno = ENOMEM;
     137            return -1;
     138          }
     139  
     140        if (desc != -1)
     141          ret = facl (desc, ACE_GETACL, ctx->ace_count, ctx->ace_entries);
     142        else
     143          ret = acl (name, ACE_GETACL, ctx->ace_count, ctx->ace_entries);
     144        if (ret < 0)
     145          {
     146            if (errno == ENOSYS || errno == EINVAL)
     147              {
     148                free (ctx->ace_entries);
     149                ctx->ace_entries = NULL;
     150                ctx->ace_count = 0;
     151                break;
     152              }
     153            else
     154              return -1;
     155          }
     156        if (ret <= ctx->ace_count)
     157          {
     158            ctx->ace_count = ret;
     159            break;
     160          }
     161        /* Huh? The number of ACL entries has increased since the last call.
     162           Repeat.  */
     163        free (ctx->ace_entries);
     164        ctx->ace_entries = NULL;
     165      }
     166  # endif
     167  
     168    for (;;)
     169      {
     170        int ret;
     171  
     172        if (desc != -1)
     173          ret = facl (desc, GETACLCNT, 0, NULL);
     174        else
     175          ret = acl (name, GETACLCNT, 0, NULL);
     176        if (ret < 0)
     177          {
     178            if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
     179              ret = 0;
     180            else
     181              return -1;
     182          }
     183        ctx->count = ret;
     184  
     185        if (ctx->count == 0)
     186          break;
     187  
     188        ctx->entries = (aclent_t *) malloc (ctx->count * sizeof (aclent_t));
     189        if (ctx->entries == NULL)
     190          {
     191            errno = ENOMEM;
     192            return -1;
     193          }
     194  
     195        if (desc != -1)
     196          ret = facl (desc, GETACL, ctx->count, ctx->entries);
     197        else
     198          ret = acl (name, GETACL, ctx->count, ctx->entries);
     199        if (ret < 0)
     200          {
     201            if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
     202              {
     203                free (ctx->entries);
     204                ctx->entries = NULL;
     205                ctx->count = 0;
     206                break;
     207              }
     208            else
     209              return -1;
     210          }
     211        if (ret <= ctx->count)
     212          {
     213            ctx->count = ret;
     214            break;
     215          }
     216        /* Huh? The number of ACL entries has increased since the last call.
     217           Repeat.  */
     218        free (ctx->entries);
     219        ctx->entries = NULL;
     220      }
     221  
     222  #elif USE_ACL && HAVE_GETACL /* HP-UX */
     223  
     224    {
     225      int ret;
     226  
     227      if (desc != -1)
     228        ret = fgetacl (desc, NACLENTRIES, ctx->entries);
     229      else
     230        ret = getacl (name, NACLENTRIES, ctx->entries);
     231      if (ret < 0)
     232        {
     233          if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
     234            ret = 0;
     235          else
     236            return -1;
     237        }
     238      else if (ret > NACLENTRIES)
     239        /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
     240        abort ();
     241      ctx->count = ret;
     242  
     243  # if HAVE_ACLV_H
     244      ret = acl ((char *) name, ACL_GET, NACLVENTRIES, ctx->aclv_entries);
     245      if (ret < 0)
     246        {
     247          if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
     248            ret = 0;
     249          else
     250            return -2;
     251        }
     252      else if (ret > NACLVENTRIES)
     253        /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation.  */
     254        abort ();
     255      ctx->aclv_count = ret;
     256  # endif
     257    }
     258  
     259  #elif USE_ACL && HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */
     260  
     261    /* TODO (see set_permissions). */
     262  
     263  #elif USE_ACL && HAVE_STATACL /* older AIX */
     264  
     265    {
     266      int ret;
     267      if (desc != -1)
     268        ret = fstatacl (desc, STX_NORMAL, &ctx->u.a, sizeof ctx->u);
     269      else
     270        ret = statacl ((char *) name, STX_NORMAL, &ctx->u.a, sizeof ctx->u);
     271      if (ret == 0)
     272        ctx->have_u = true;
     273    }
     274  
     275  #elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
     276  
     277    {
     278      int ret = acl ((char *) name, ACL_GET, NACLENTRIES, ctx->entries);
     279      if (ret < 0)
     280        return -1;
     281      else if (ret > NACLENTRIES)
     282        /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
     283        abort ();
     284      ctx->count = ret;
     285    }
     286  
     287  #endif
     288  
     289    return 0;
     290  
     291  }