Home | History | Annotate | Download | only in lib
      1 /* copy-acl.c - copy access control list from one file to another file
      2 
      3    Copyright (C) 2002-2003, 2005-2008 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 <http://www.gnu.org/licenses/>.
     17 
     18    Written by Paul Eggert, Andreas Grnbacher, and Bruno Haible.  */
     19 
     20 #include <config.h>
     21 
     22 #include "acl.h"
     23 
     24 #include "acl-internal.h"
     25 
     26 #include "gettext.h"
     27 #define _(msgid) gettext (msgid)
     28 
     29 
     30 /* Copy access control lists from one file to another. If SOURCE_DESC is
     31    a valid file descriptor, use file descriptor operations, else use
     32    filename based operations on SRC_NAME. Likewise for DEST_DESC and
     33    DST_NAME.
     34    If access control lists are not available, fchmod the target file to
     35    MODE.  Also sets the non-permission bits of the destination file
     36    (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
     37    Return 0 if successful.
     38    Return -2 and set errno for an error relating to the source file.
     39    Return -1 and set errno for an error relating to the destination file.  */
     40 
     41 static int
     42 qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
     43 	   int dest_desc, mode_t mode)
     44 {
     45 #if USE_ACL && HAVE_ACL_GET_FILE
     46   /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
     47   /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
     48 # if MODE_INSIDE_ACL
     49   /* Linux, FreeBSD, IRIX, Tru64 */
     50 
     51   acl_t acl;
     52   int ret;
     53 
     54   if (HAVE_ACL_GET_FD && source_desc != -1)
     55     acl = acl_get_fd (source_desc);
     56   else
     57     acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
     58   if (acl == NULL)
     59     {
     60       if (ACL_NOT_WELL_SUPPORTED (errno))
     61 	return qset_acl (dst_name, dest_desc, mode);
     62       else
     63 	return -2;
     64     }
     65 
     66   if (HAVE_ACL_SET_FD && dest_desc != -1)
     67     ret = acl_set_fd (dest_desc, acl);
     68   else
     69     ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
     70   if (ret != 0)
     71     {
     72       int saved_errno = errno;
     73 
     74       if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_access_nontrivial (acl))
     75 	{
     76 	  acl_free (acl);
     77 	  return chmod_or_fchmod (dst_name, dest_desc, mode);
     78 	}
     79       else
     80 	{
     81 	  acl_free (acl);
     82 	  chmod_or_fchmod (dst_name, dest_desc, mode);
     83 	  errno = saved_errno;
     84 	  return -1;
     85 	}
     86     }
     87   else
     88     acl_free (acl);
     89 
     90   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
     91     {
     92       /* We did not call chmod so far, and either the mode and the ACL are
     93 	 separate or special bits are to be set which don't fit into ACLs.  */
     94 
     95       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
     96 	return -1;
     97     }
     98 
     99   if (S_ISDIR (mode))
    100     {
    101       acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
    102       if (acl == NULL)
    103 	return -2;
    104 
    105       if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
    106 	{
    107 	  int saved_errno = errno;
    108 
    109 	  acl_free (acl);
    110 	  errno = saved_errno;
    111 	  return -1;
    112 	}
    113       else
    114 	acl_free (acl);
    115     }
    116   return 0;
    117 
    118 # else /* !MODE_INSIDE_ACL */
    119   /* MacOS X */
    120 
    121 #  if !HAVE_ACL_TYPE_EXTENDED
    122 #   error Must have ACL_TYPE_EXTENDED
    123 #  endif
    124 
    125   /* On MacOS X,  acl_get_file (name, ACL_TYPE_ACCESS)
    126      and          acl_get_file (name, ACL_TYPE_DEFAULT)
    127      always return NULL / EINVAL.  You have to use
    128 		  acl_get_file (name, ACL_TYPE_EXTENDED)
    129      or           acl_get_fd (open (name, ...))
    130      to retrieve an ACL.
    131      On the other hand,
    132 		  acl_set_file (name, ACL_TYPE_ACCESS, acl)
    133      and          acl_set_file (name, ACL_TYPE_DEFAULT, acl)
    134      have the same effect as
    135 		  acl_set_file (name, ACL_TYPE_EXTENDED, acl):
    136      Each of these calls sets the file's ACL.  */
    137 
    138   acl_t acl;
    139   int ret;
    140 
    141   if (HAVE_ACL_GET_FD && source_desc != -1)
    142     acl = acl_get_fd (source_desc);
    143   else
    144     acl = acl_get_file (src_name, ACL_TYPE_EXTENDED);
    145   if (acl == NULL)
    146     {
    147       if (ACL_NOT_WELL_SUPPORTED (errno))
    148 	return qset_acl (dst_name, dest_desc, mode);
    149       else
    150 	return -2;
    151     }
    152 
    153   if (HAVE_ACL_SET_FD && dest_desc != -1)
    154     ret = acl_set_fd (dest_desc, acl);
    155   else
    156     ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl);
    157   if (ret != 0)
    158     {
    159       int saved_errno = errno;
    160 
    161       if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_extended_nontrivial (acl))
    162 	{
    163 	  acl_free (acl);
    164 	  return chmod_or_fchmod (dst_name, dest_desc, mode);
    165 	}
    166       else
    167 	{
    168 	  acl_free (acl);
    169 	  chmod_or_fchmod (dst_name, dest_desc, mode);
    170 	  errno = saved_errno;
    171 	  return -1;
    172 	}
    173     }
    174   else
    175     acl_free (acl);
    176 
    177   /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly.  */
    178   return chmod_or_fchmod (dst_name, dest_desc, mode);
    179 
    180 # endif
    181 
    182 #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
    183 
    184 # if defined ACL_NO_TRIVIAL
    185   /* Solaris 10 (newer version), which has additional API declared in
    186      <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
    187      acl_fromtext, ...).  */
    188 
    189   int ret;
    190   acl_t *aclp = NULL;
    191   ret = (source_desc < 0
    192 	 ? acl_get (src_name, ACL_NO_TRIVIAL, &aclp)
    193 	 : facl_get (source_desc, ACL_NO_TRIVIAL, &aclp));
    194   if (ret != 0 && errno != ENOSYS)
    195     return -2;
    196 
    197   ret = qset_acl (dst_name, dest_desc, mode);
    198   if (ret != 0)
    199     return -1;
    200 
    201   if (aclp)
    202     {
    203       ret = (dest_desc < 0
    204 	     ? acl_set (dst_name, aclp)
    205 	     : facl_set (dest_desc, aclp));
    206       if (ret != 0)
    207 	{
    208 	  int saved_errno = errno;
    209 
    210 	  acl_free (aclp);
    211 	  errno = saved_errno;
    212 	  return -1;
    213 	}
    214       acl_free (aclp);
    215     }
    216 
    217   return 0;
    218 
    219 # else /* Solaris, Cygwin, general case */
    220 
    221   /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
    222      of Unixware.  The acl() call returns the access and default ACL both
    223      at once.  */
    224 #  ifdef ACE_GETACL
    225   int ace_count;
    226   ace_t *ace_entries;
    227 #  endif
    228   int count;
    229   aclent_t *entries;
    230   int did_chmod;
    231   int saved_errno;
    232   int ret;
    233 
    234 #  ifdef ACE_GETACL
    235   /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
    236      file systems (whereas the other ones are used in UFS file systems).
    237      There is an API
    238        pathconf (name, _PC_ACL_ENABLED)
    239        fpathconf (desc, _PC_ACL_ENABLED)
    240      that allows to determine which of the two kinds of ACLs is supported
    241      for the given file.  But some file systems may implement this call
    242      incorrectly, so better not use it.
    243      When fetching the source ACL, we simply fetch both ACL types.
    244      When setting the destination ACL, we try either ACL types, assuming
    245      that the kernel will translate the ACL from one form to the other.
    246      (See in <http://docs.sun.com/app/docs/doc/819-2241/6n4huc7ia?l=en&a=view>
    247      the description of ENOTSUP.)  */
    248   for (;;)
    249     {
    250       ace_count = (source_desc != -1
    251 		   ? facl (source_desc, ACE_GETACLCNT, 0, NULL)
    252 		   : acl (src_name, ACE_GETACLCNT, 0, NULL));
    253 
    254       if (ace_count < 0)
    255 	{
    256 	  if (errno == ENOSYS || errno == EINVAL)
    257 	    {
    258 	      ace_count = 0;
    259 	      ace_entries = NULL;
    260 	      break;
    261 	    }
    262 	  else
    263 	    return -2;
    264 	}
    265 
    266       if (ace_count == 0)
    267 	{
    268 	  ace_entries = NULL;
    269 	  break;
    270 	}
    271 
    272       ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t));
    273       if (ace_entries == NULL)
    274 	{
    275 	  errno = ENOMEM;
    276 	  return -2;
    277 	}
    278 
    279       if ((source_desc != -1
    280 	   ? facl (source_desc, ACE_GETACL, ace_count, ace_entries)
    281 	   : acl (src_name, ACE_GETACL, ace_count, ace_entries))
    282 	  == ace_count)
    283 	break;
    284       /* Huh? The number of ACL entries changed since the last call.
    285 	 Repeat.  */
    286     }
    287 #  endif
    288 
    289   for (;;)
    290     {
    291       count = (source_desc != -1
    292 	       ? facl (source_desc, GETACLCNT, 0, NULL)
    293 	       : acl (src_name, GETACLCNT, 0, NULL));
    294 
    295       if (count < 0)
    296 	{
    297 	  if (errno == ENOSYS || errno == ENOTSUP)
    298 	    {
    299 	      count = 0;
    300 	      entries = NULL;
    301 	      break;
    302 	    }
    303 	  else
    304 	    return -2;
    305 	}
    306 
    307       if (count == 0)
    308 	{
    309 	  entries = NULL;
    310 	  break;
    311 	}
    312 
    313       entries = (aclent_t *) malloc (count * sizeof (aclent_t));
    314       if (entries == NULL)
    315 	{
    316 	  errno = ENOMEM;
    317 	  return -2;
    318 	}
    319 
    320       if ((source_desc != -1
    321 	   ? facl (source_desc, GETACL, count, entries)
    322 	   : acl (src_name, GETACL, count, entries))
    323 	  == count)
    324 	break;
    325       /* Huh? The number of ACL entries changed since the last call.
    326 	 Repeat.  */
    327     }
    328 
    329   /* Is there an ACL of either kind?  */
    330 #  ifdef ACE_GETACL
    331   if (ace_count == 0)
    332 #  endif
    333     if (count == 0)
    334       return qset_acl (dst_name, dest_desc, mode);
    335 
    336   did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
    337   saved_errno = 0; /* the first non-ignorable error code */
    338 
    339   if (!MODE_INSIDE_ACL)
    340     {
    341       /* On Cygwin, it is necessary to call chmod before acl, because
    342 	 chmod can change the contents of the ACL (in ways that don't
    343 	 change the allowed accesses, but still visible).  */
    344       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
    345 	saved_errno = errno;
    346       did_chmod = 1;
    347     }
    348 
    349   /* If both ace_entries and entries are available, try SETACL before
    350      ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
    351      can.  */
    352 
    353   if (count > 0)
    354     {
    355       ret = (dest_desc != -1
    356 	     ? facl (dest_desc, SETACL, count, entries)
    357 	     : acl (dst_name, SETACL, count, entries));
    358       if (ret < 0 && saved_errno == 0)
    359 	{
    360 	  saved_errno = errno;
    361 	  if (errno == ENOSYS && !acl_nontrivial (count, entries))
    362 	    saved_errno = 0;
    363 	}
    364       else
    365 	did_chmod = 1;
    366     }
    367   free (entries);
    368 
    369 #  ifdef ACE_GETACL
    370   if (ace_count > 0)
    371     {
    372       ret = (dest_desc != -1
    373 	     ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries)
    374 	     : acl (dst_name, ACE_SETACL, ace_count, ace_entries));
    375       if (ret < 0 && saved_errno == 0)
    376 	{
    377 	  saved_errno = errno;
    378 	  if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
    379 	      && !acl_ace_nontrivial (ace_count, ace_entries))
    380 	    saved_errno = 0;
    381 	}
    382     }
    383   free (ace_entries);
    384 #  endif
    385 
    386   if (MODE_INSIDE_ACL
    387       && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
    388     {
    389       /* We did not call chmod so far, and either the mode and the ACL are
    390 	 separate or special bits are to be set which don't fit into ACLs.  */
    391 
    392       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
    393 	{
    394 	  if (saved_errno == 0)
    395 	    saved_errno = errno;
    396 	}
    397     }
    398 
    399   if (saved_errno)
    400     {
    401       errno = saved_errno;
    402       return -1;
    403     }
    404   return 0;
    405 
    406 # endif
    407 
    408 #elif USE_ACL && HAVE_GETACL /* HP-UX */
    409 
    410   int count;
    411   struct acl_entry entries[NACLENTRIES];
    412   int ret;
    413 
    414   for (;;)
    415     {
    416       count = (source_desc != -1
    417 	       ? fgetacl (source_desc, 0, NULL)
    418 	       : getacl (src_name, 0, NULL));
    419 
    420       if (count < 0)
    421 	{
    422 	  if (errno == ENOSYS || errno == EOPNOTSUPP)
    423 	    {
    424 	      count = 0;
    425 	      break;
    426 	    }
    427 	  else
    428 	    return -2;
    429 	}
    430 
    431       if (count == 0)
    432 	break;
    433 
    434       if (count > NACLENTRIES)
    435 	/* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
    436 	abort ();
    437 
    438       if ((source_desc != -1
    439 	   ? fgetacl (source_desc, count, entries)
    440 	   : getacl (src_name, count, entries))
    441 	  == count)
    442 	break;
    443       /* Huh? The number of ACL entries changed since the last call.
    444 	 Repeat.  */
    445     }
    446 
    447   if (count == 0)
    448     return qset_acl (dst_name, dest_desc, mode);
    449 
    450   ret = (dest_desc != -1
    451 	 ? fsetacl (dest_desc, count, entries)
    452 	 : setacl (dst_name, count, entries));
    453   if (ret < 0)
    454     {
    455       int saved_errno = errno;
    456 
    457       if (errno == ENOSYS || errno == EOPNOTSUPP)
    458 	{
    459 	  struct stat source_statbuf;
    460 
    461 	  if ((source_desc != -1
    462 	       ? fstat (source_desc, &source_statbuf)
    463 	       : stat (src_name, &source_statbuf)) == 0)
    464 	    {
    465 	      if (!acl_nontrivial (count, entries, &source_statbuf))
    466 		return chmod_or_fchmod (dst_name, dest_desc, mode);
    467 	    }
    468 	  else
    469 	    saved_errno = errno;
    470 	}
    471 
    472       chmod_or_fchmod (dst_name, dest_desc, mode);
    473       errno = saved_errno;
    474       return -1;
    475     }
    476 
    477   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
    478     {
    479       /* We did not call chmod so far, and either the mode and the ACL are
    480 	 separate or special bits are to be set which don't fit into ACLs.  */
    481 
    482       return chmod_or_fchmod (dst_name, dest_desc, mode);
    483     }
    484   return 0;
    485 
    486 #elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
    487 
    488   /* TODO */
    489 
    490 #elif USE_ACL && HAVE_STATACL /* older AIX */
    491 
    492   union { struct acl a; char room[4096]; } u;
    493   int ret;
    494 
    495   if ((source_desc != -1
    496        ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u))
    497        : statacl (src_name, STX_NORMAL, &u.a, sizeof (u)))
    498       < 0)
    499     return -2;
    500 
    501   ret = (dest_desc != -1
    502 	 ? fchacl (dest_desc, &u.a, u.a.acl_len)
    503 	 : chacl (dst_name, &u.a, u.a.acl_len));
    504   if (ret < 0)
    505     {
    506       int saved_errno = errno;
    507 
    508       chmod_or_fchmod (dst_name, dest_desc, mode);
    509       errno = saved_errno;
    510       return -1;
    511     }
    512 
    513   /* No need to call chmod_or_fchmod at this point, since the mode bits
    514      S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL.  */
    515 
    516   return 0;
    517 
    518 #else
    519 
    520   return qset_acl (dst_name, dest_desc, mode);
    521 
    522 #endif
    523 }
    524 
    525 
    526 /* Copy access control lists from one file to another. If SOURCE_DESC is
    527    a valid file descriptor, use file descriptor operations, else use
    528    filename based operations on SRC_NAME. Likewise for DEST_DESC and
    529    DST_NAME.
    530    If access control lists are not available, fchmod the target file to
    531    MODE.  Also sets the non-permission bits of the destination file
    532    (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
    533    Return 0 if successful, otherwise output a diagnostic and return -1.  */
    534 
    535 int
    536 copy_acl (const char *src_name, int source_desc, const char *dst_name,
    537 	  int dest_desc, mode_t mode)
    538 {
    539   int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode);
    540   switch (ret)
    541     {
    542     case -2:
    543       error (0, errno, "%s", quote (src_name));
    544       return -1;
    545 
    546     case -1:
    547       error (0, errno, _("preserving permissions for %s"), quote (dst_name));
    548       return -1;
    549 
    550     default:
    551       return 0;
    552     }
    553 }
    554