(root)/
sed-4.9/
gnulib-tests/
file-has-acl.c
       1  /* Test whether a file has a nontrivial ACL.  -*- coding: utf-8 -*-
       2  
       3     Copyright (C) 2002-2003, 2005-2022 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  /* Without this pragma, gcc 4.7.0 20120126 may suggest that the
      21     file_has_acl function might be candidate for attribute 'const'  */
      22  #if (__GNUC__ == 4 && 6 <= __GNUC_MINOR__) || 4 < __GNUC__
      23  # pragma GCC diagnostic ignored "-Wsuggest-attribute=const"
      24  #endif
      25  
      26  #include <config.h>
      27  
      28  #include "acl.h"
      29  
      30  #include "acl-internal.h"
      31  
      32  #if GETXATTR_WITH_POSIX_ACLS
      33  # include <sys/xattr.h>
      34  # include <linux/xattr.h>
      35  #endif
      36  
      37  /* Return 1 if NAME has a nontrivial access control list,
      38     0 if ACLs are not supported, or if NAME has no or only a base ACL,
      39     and -1 (setting errno) on error.  Note callers can determine
      40     if ACLs are not supported as errno is set in that case also.
      41     SB must be set to the stat buffer of NAME,
      42     obtained through stat() or lstat().  */
      43  
      44  int
      45  file_has_acl (char const *name, struct stat const *sb)
      46  {
      47  #if USE_ACL
      48    if (! S_ISLNK (sb->st_mode))
      49      {
      50  
      51  # if GETXATTR_WITH_POSIX_ACLS
      52  
      53        ssize_t ret;
      54  
      55        ret = getxattr (name, XATTR_NAME_POSIX_ACL_ACCESS, NULL, 0);
      56        if (ret < 0 && errno == ENODATA)
      57          ret = 0;
      58        else if (ret > 0)
      59          return 1;
      60  
      61        if (ret == 0 && S_ISDIR (sb->st_mode))
      62          {
      63            ret = getxattr (name, XATTR_NAME_POSIX_ACL_DEFAULT, NULL, 0);
      64            if (ret < 0 && errno == ENODATA)
      65              ret = 0;
      66            else if (ret > 0)
      67              return 1;
      68          }
      69  
      70        if (ret < 0)
      71          return - acl_errno_valid (errno);
      72        return ret;
      73  
      74  # elif HAVE_ACL_GET_FILE
      75  
      76        /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
      77        /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
      78        int ret;
      79  
      80        if (HAVE_ACL_EXTENDED_FILE) /* Linux */
      81          {
      82            /* On Linux, acl_extended_file is an optimized function: It only
      83               makes two calls to getxattr(), one for ACL_TYPE_ACCESS, one for
      84               ACL_TYPE_DEFAULT.  */
      85            ret = acl_extended_file (name);
      86          }
      87        else /* FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
      88          {
      89  #  if HAVE_ACL_TYPE_EXTENDED /* Mac OS X */
      90            /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
      91               and acl_get_file (name, ACL_TYPE_DEFAULT)
      92               always return NULL / EINVAL.  There is no point in making
      93               these two useless calls.  The real ACL is retrieved through
      94               acl_get_file (name, ACL_TYPE_EXTENDED).  */
      95            acl_t acl = acl_get_file (name, ACL_TYPE_EXTENDED);
      96            if (acl)
      97              {
      98                ret = acl_extended_nontrivial (acl);
      99                acl_free (acl);
     100              }
     101            else
     102              ret = -1;
     103  #  else /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
     104            acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
     105            if (acl)
     106              {
     107                int saved_errno;
     108  
     109                ret = acl_access_nontrivial (acl);
     110                saved_errno = errno;
     111                acl_free (acl);
     112                errno = saved_errno;
     113  #   if HAVE_ACL_FREE_TEXT /* Tru64 */
     114                /* On OSF/1, acl_get_file (name, ACL_TYPE_DEFAULT) always
     115                   returns NULL with errno not set.  There is no point in
     116                   making this call.  */
     117  #   else /* FreeBSD, IRIX, Cygwin >= 2.5 */
     118                /* On Linux, FreeBSD, IRIX, acl_get_file (name, ACL_TYPE_ACCESS)
     119                   and acl_get_file (name, ACL_TYPE_DEFAULT) on a directory
     120                   either both succeed or both fail; it depends on the
     121                   file system.  Therefore there is no point in making the second
     122                   call if the first one already failed.  */
     123                if (ret == 0 && S_ISDIR (sb->st_mode))
     124                  {
     125                    acl = acl_get_file (name, ACL_TYPE_DEFAULT);
     126                    if (acl)
     127                      {
     128  #    ifdef __CYGWIN__ /* Cygwin >= 2.5 */
     129                        ret = acl_access_nontrivial (acl);
     130                        saved_errno = errno;
     131                        acl_free (acl);
     132                        errno = saved_errno;
     133  #    else
     134                        ret = (0 < acl_entries (acl));
     135                        acl_free (acl);
     136  #    endif
     137                      }
     138                    else
     139                      ret = -1;
     140                  }
     141  #   endif
     142              }
     143            else
     144              ret = -1;
     145  #  endif
     146          }
     147        if (ret < 0)
     148          return - acl_errno_valid (errno);
     149        return ret;
     150  
     151  # elif HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
     152  
     153  #  if defined ACL_NO_TRIVIAL
     154  
     155        /* Solaris 10 (newer version), which has additional API declared in
     156           <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
     157           acl_fromtext, ...).  */
     158        return acl_trivial (name);
     159  
     160  #  else /* Solaris, Cygwin, general case */
     161  
     162        /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
     163           of Unixware.  The acl() call returns the access and default ACL both
     164           at once.  */
     165        {
     166          /* Initially, try to read the entries into a stack-allocated buffer.
     167             Use malloc if it does not fit.  */
     168          enum
     169            {
     170              alloc_init = 4000 / sizeof (aclent_t), /* >= 3 */
     171              alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (aclent_t))
     172            };
     173          aclent_t buf[alloc_init];
     174          size_t alloc = alloc_init;
     175          aclent_t *entries = buf;
     176          aclent_t *malloced = NULL;
     177          int count;
     178  
     179          for (;;)
     180            {
     181              count = acl (name, GETACL, alloc, entries);
     182              if (count < 0 && errno == ENOSPC)
     183                {
     184                  /* Increase the size of the buffer.  */
     185                  free (malloced);
     186                  if (alloc > alloc_max / 2)
     187                    {
     188                      errno = ENOMEM;
     189                      return -1;
     190                    }
     191                  alloc = 2 * alloc; /* <= alloc_max */
     192                  entries = malloced =
     193                    (aclent_t *) malloc (alloc * sizeof (aclent_t));
     194                  if (entries == NULL)
     195                    {
     196                      errno = ENOMEM;
     197                      return -1;
     198                    }
     199                  continue;
     200                }
     201              break;
     202            }
     203          if (count < 0)
     204            {
     205              if (errno == ENOSYS || errno == ENOTSUP)
     206                ;
     207              else
     208                {
     209                  free (malloced);
     210                  return -1;
     211                }
     212            }
     213          else if (count == 0)
     214            ;
     215          else
     216            {
     217              /* Don't use MIN_ACL_ENTRIES:  It's set to 4 on Cygwin, but Cygwin
     218                 returns only 3 entries for files with no ACL.  But this is safe:
     219                 If there are more than 4 entries, there cannot be only the
     220                 "user::", "group::", "other:", and "mask:" entries.  */
     221              if (count > 4)
     222                {
     223                  free (malloced);
     224                  return 1;
     225                }
     226  
     227              if (acl_nontrivial (count, entries))
     228                {
     229                  free (malloced);
     230                  return 1;
     231                }
     232            }
     233          free (malloced);
     234        }
     235  
     236  #   ifdef ACE_GETACL
     237        /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
     238           file systems (whereas the other ones are used in UFS file systems).  */
     239        {
     240          /* Initially, try to read the entries into a stack-allocated buffer.
     241             Use malloc if it does not fit.  */
     242          enum
     243            {
     244              alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
     245              alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
     246            };
     247          ace_t buf[alloc_init];
     248          size_t alloc = alloc_init;
     249          ace_t *entries = buf;
     250          ace_t *malloced = NULL;
     251          int count;
     252  
     253          for (;;)
     254            {
     255              count = acl (name, ACE_GETACL, alloc, entries);
     256              if (count < 0 && errno == ENOSPC)
     257                {
     258                  /* Increase the size of the buffer.  */
     259                  free (malloced);
     260                  if (alloc > alloc_max / 2)
     261                    {
     262                      errno = ENOMEM;
     263                      return -1;
     264                    }
     265                  alloc = 2 * alloc; /* <= alloc_max */
     266                  entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
     267                  if (entries == NULL)
     268                    {
     269                      errno = ENOMEM;
     270                      return -1;
     271                    }
     272                  continue;
     273                }
     274              break;
     275            }
     276          if (count < 0)
     277            {
     278              if (errno == ENOSYS || errno == EINVAL)
     279                ;
     280              else
     281                {
     282                  free (malloced);
     283                  return -1;
     284                }
     285            }
     286          else if (count == 0)
     287            ;
     288          else
     289            {
     290              /* In the old (original Solaris 10) convention:
     291                 If there are more than 3 entries, there cannot be only the
     292                 ACE_OWNER, ACE_GROUP, ACE_OTHER entries.
     293                 In the newer Solaris 10 and Solaris 11 convention:
     294                 If there are more than 6 entries, there cannot be only the
     295                 ACE_OWNER, ACE_GROUP, ACE_EVERYONE entries, each once with
     296                 NEW_ACE_ACCESS_ALLOWED_ACE_TYPE and once with
     297                 NEW_ACE_ACCESS_DENIED_ACE_TYPE.  */
     298              if (count > 6)
     299                {
     300                  free (malloced);
     301                  return 1;
     302                }
     303  
     304              if (acl_ace_nontrivial (count, entries))
     305                {
     306                  free (malloced);
     307                  return 1;
     308                }
     309            }
     310          free (malloced);
     311        }
     312  #   endif
     313  
     314        return 0;
     315  #  endif
     316  
     317  # elif HAVE_GETACL /* HP-UX */
     318  
     319        {
     320          struct acl_entry entries[NACLENTRIES];
     321          int count;
     322  
     323          count = getacl (name, NACLENTRIES, entries);
     324  
     325          if (count < 0)
     326            {
     327              /* ENOSYS is seen on newer HP-UX versions.
     328                 EOPNOTSUPP is typically seen on NFS mounts.
     329                 ENOTSUP was seen on Quantum StorNext file systems (cvfs).  */
     330              if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
     331                ;
     332              else
     333                return -1;
     334            }
     335          else if (count == 0)
     336            return 0;
     337          else /* count > 0 */
     338            {
     339              if (count > NACLENTRIES)
     340                /* If NACLENTRIES cannot be trusted, use dynamic memory
     341                   allocation.  */
     342                abort ();
     343  
     344              /* If there are more than 3 entries, there cannot be only the
     345                 (uid,%), (%,gid), (%,%) entries.  */
     346              if (count > 3)
     347                return 1;
     348  
     349              {
     350                struct stat statbuf;
     351  
     352                if (stat (name, &statbuf) == -1 && errno != EOVERFLOW)
     353                  return -1;
     354  
     355                return acl_nontrivial (count, entries);
     356              }
     357            }
     358        }
     359  
     360  #  if HAVE_ACLV_H /* HP-UX >= 11.11 */
     361  
     362        {
     363          struct acl entries[NACLVENTRIES];
     364          int count;
     365  
     366          count = acl ((char *) name, ACL_GET, NACLVENTRIES, entries);
     367  
     368          if (count < 0)
     369            {
     370              /* EOPNOTSUPP is seen on NFS in HP-UX 11.11, 11.23.
     371                 EINVAL is seen on NFS in HP-UX 11.31.  */
     372              if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
     373                ;
     374              else
     375                return -1;
     376            }
     377          else if (count == 0)
     378            return 0;
     379          else /* count > 0 */
     380            {
     381              if (count > NACLVENTRIES)
     382                /* If NACLVENTRIES cannot be trusted, use dynamic memory
     383                   allocation.  */
     384                abort ();
     385  
     386              /* If there are more than 4 entries, there cannot be only the
     387                 four base ACL entries.  */
     388              if (count > 4)
     389                return 1;
     390  
     391              return aclv_nontrivial (count, entries);
     392            }
     393        }
     394  
     395  #  endif
     396  
     397  # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
     398  
     399        acl_type_t type;
     400        char aclbuf[1024];
     401        void *acl = aclbuf;
     402        size_t aclsize = sizeof (aclbuf);
     403        mode_t mode;
     404  
     405        for (;;)
     406          {
     407            /* The docs say that type being 0 is equivalent to ACL_ANY, but it
     408               is not true, in AIX 5.3.  */
     409            type.u64 = ACL_ANY;
     410            if (aclx_get (name, 0, &type, aclbuf, &aclsize, &mode) >= 0)
     411              break;
     412            if (errno == ENOSYS)
     413              return 0;
     414            if (errno != ENOSPC)
     415              {
     416                if (acl != aclbuf)
     417                  free (acl);
     418                return -1;
     419              }
     420            aclsize = 2 * aclsize;
     421            if (acl != aclbuf)
     422              free (acl);
     423            acl = malloc (aclsize);
     424            if (acl == NULL)
     425              {
     426                errno = ENOMEM;
     427                return -1;
     428              }
     429          }
     430  
     431        if (type.u64 == ACL_AIXC)
     432          {
     433            int result = acl_nontrivial ((struct acl *) acl);
     434            if (acl != aclbuf)
     435              free (acl);
     436            return result;
     437          }
     438        else if (type.u64 == ACL_NFS4)
     439          {
     440            int result = acl_nfs4_nontrivial ((nfs4_acl_int_t *) acl);
     441            if (acl != aclbuf)
     442              free (acl);
     443            return result;
     444          }
     445        else
     446          {
     447            /* A newer type of ACL has been introduced in the system.
     448               We should better support it.  */
     449            if (acl != aclbuf)
     450              free (acl);
     451            errno = EINVAL;
     452            return -1;
     453          }
     454  
     455  # elif HAVE_STATACL /* older AIX */
     456  
     457        union { struct acl a; char room[4096]; } u;
     458  
     459        if (statacl ((char *) name, STX_NORMAL, &u.a, sizeof (u)) < 0)
     460          return -1;
     461  
     462        return acl_nontrivial (&u.a);
     463  
     464  # elif HAVE_ACLSORT /* NonStop Kernel */
     465  
     466        {
     467          struct acl entries[NACLENTRIES];
     468          int count;
     469  
     470          count = acl ((char *) name, ACL_GET, NACLENTRIES, entries);
     471  
     472          if (count < 0)
     473            {
     474              if (errno == ENOSYS || errno == ENOTSUP)
     475                ;
     476              else
     477                return -1;
     478            }
     479          else if (count == 0)
     480            return 0;
     481          else /* count > 0 */
     482            {
     483              if (count > NACLENTRIES)
     484                /* If NACLENTRIES cannot be trusted, use dynamic memory
     485                   allocation.  */
     486                abort ();
     487  
     488              /* If there are more than 4 entries, there cannot be only the
     489                 four base ACL entries.  */
     490              if (count > 4)
     491                return 1;
     492  
     493              return acl_nontrivial (count, entries);
     494            }
     495        }
     496  
     497  # endif
     498      }
     499  #endif
     500  
     501    return 0;
     502  }