1 /* copy-acl.c - copy access control list from one file to another file
3 Copyright (C) 2002-2003, 2005-2011 Free Software Foundation, Inc.
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.
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.
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/>.
18 Written by Paul Eggert, Andreas Grünbacher, and Bruno Haible. */
24 #include "acl-internal.h"
27 #define _(msgid) gettext (msgid)
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
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. */
42 qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
43 int dest_desc, mode_t mode)
45 #if USE_ACL && HAVE_ACL_GET_FILE
46 /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
47 /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
49 /* Linux, FreeBSD, IRIX, Tru64 */
54 if (HAVE_ACL_GET_FD && source_desc != -1)
55 acl = acl_get_fd (source_desc);
57 acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
60 if (ACL_NOT_WELL_SUPPORTED (errno))
61 return qset_acl (dst_name, dest_desc, mode);
66 if (HAVE_ACL_SET_FD && dest_desc != -1)
67 ret = acl_set_fd (dest_desc, acl);
69 ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
72 int saved_errno = errno;
74 if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_access_nontrivial (acl))
77 return chmod_or_fchmod (dst_name, dest_desc, mode);
82 chmod_or_fchmod (dst_name, dest_desc, mode);
90 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
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. */
95 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
101 acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
105 if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
107 int saved_errno = errno;
118 # else /* !MODE_INSIDE_ACL */
121 # if !HAVE_ACL_TYPE_EXTENDED
122 # error Must have ACL_TYPE_EXTENDED
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, ...))
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. */
141 if (HAVE_ACL_GET_FD && source_desc != -1)
142 acl = acl_get_fd (source_desc);
144 acl = acl_get_file (src_name, ACL_TYPE_EXTENDED);
147 if (ACL_NOT_WELL_SUPPORTED (errno))
148 return qset_acl (dst_name, dest_desc, mode);
153 if (HAVE_ACL_SET_FD && dest_desc != -1)
154 ret = acl_set_fd (dest_desc, acl);
156 ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl);
159 int saved_errno = errno;
161 if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_extended_nontrivial (acl))
164 return chmod_or_fchmod (dst_name, dest_desc, mode);
169 chmod_or_fchmod (dst_name, dest_desc, mode);
177 /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
178 return chmod_or_fchmod (dst_name, dest_desc, mode);
182 #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
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, ...). */
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)
197 ret = qset_acl (dst_name, dest_desc, mode);
204 ? acl_set (dst_name, aclp)
205 : facl_set (dest_desc, aclp));
208 int saved_errno = errno;
219 # else /* Solaris, Cygwin, general case */
221 /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
222 of Unixware. The acl() call returns the access and default ACL both
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).
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.) */
250 ace_count = (source_desc != -1
251 ? facl (source_desc, ACE_GETACLCNT, 0, NULL)
252 : acl (src_name, ACE_GETACLCNT, 0, NULL));
256 if (errno == ENOSYS || errno == EINVAL)
272 ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t));
273 if (ace_entries == NULL)
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))
284 /* Huh? The number of ACL entries changed since the last call.
291 count = (source_desc != -1
292 ? facl (source_desc, GETACLCNT, 0, NULL)
293 : acl (src_name, GETACLCNT, 0, NULL));
297 if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
313 entries = (aclent_t *) malloc (count * sizeof (aclent_t));
320 if ((source_desc != -1
321 ? facl (source_desc, GETACL, count, entries)
322 : acl (src_name, GETACL, count, entries))
325 /* Huh? The number of ACL entries changed since the last call.
329 /* Is there an ACL of either kind? */
334 return qset_acl (dst_name, dest_desc, mode);
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 */
339 if (!MODE_INSIDE_ACL)
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)
349 /* If both ace_entries and entries are available, try SETACL before
350 ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
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)
361 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
362 && !acl_nontrivial (count, entries))
373 ret = (dest_desc != -1
374 ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries)
375 : acl (dst_name, ACE_SETACL, ace_count, ace_entries));
376 if (ret < 0 && saved_errno == 0)
379 if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
380 && !acl_ace_nontrivial (ace_count, ace_entries))
388 && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
390 /* We did not call chmod so far, and either the mode and the ACL are
391 separate or special bits are to be set which don't fit into ACLs. */
393 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
395 if (saved_errno == 0)
409 #elif USE_ACL && HAVE_GETACL /* HP-UX */
412 struct acl_entry entries[NACLENTRIES];
415 struct acl aclv_entries[NACLVENTRIES];
423 count = (source_desc != -1
424 ? fgetacl (source_desc, 0, NULL)
425 : getacl (src_name, 0, NULL));
429 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
441 if (count > NACLENTRIES)
442 /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
445 if ((source_desc != -1
446 ? fgetacl (source_desc, count, entries)
447 : getacl (src_name, count, entries))
450 /* Huh? The number of ACL entries changed since the last call.
457 aclv_count = acl ((char *) src_name, ACL_CNT, NACLVENTRIES, aclv_entries);
461 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
473 if (aclv_count > NACLVENTRIES)
474 /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */
477 if (acl ((char *) src_name, ACL_GET, aclv_count, aclv_entries)
480 /* Huh? The number of ACL entries changed since the last call.
489 return qset_acl (dst_name, dest_desc, mode);
491 did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
492 saved_errno = 0; /* the first non-ignorable error code */
496 ret = (dest_desc != -1
497 ? fsetacl (dest_desc, count, entries)
498 : setacl (dst_name, count, entries));
499 if (ret < 0 && saved_errno == 0)
502 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
504 struct stat source_statbuf;
506 if ((source_desc != -1
507 ? fstat (source_desc, &source_statbuf)
508 : stat (src_name, &source_statbuf)) == 0)
510 if (!acl_nontrivial (count, entries, &source_statbuf))
524 ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries);
525 if (ret < 0 && saved_errno == 0)
528 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
530 if (!aclv_nontrivial (aclv_count, aclv_entries))
539 if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
541 /* We did not call chmod so far, and special bits are to be set which
542 don't fit into ACLs. */
544 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
546 if (saved_errno == 0)
558 #elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
562 #elif USE_ACL && HAVE_STATACL /* older AIX */
564 union { struct acl a; char room[4096]; } u;
567 if ((source_desc != -1
568 ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u))
569 : statacl (src_name, STX_NORMAL, &u.a, sizeof (u)))
573 ret = (dest_desc != -1
574 ? fchacl (dest_desc, &u.a, u.a.acl_len)
575 : chacl (dst_name, &u.a, u.a.acl_len));
578 int saved_errno = errno;
580 chmod_or_fchmod (dst_name, dest_desc, mode);
585 /* No need to call chmod_or_fchmod at this point, since the mode bits
586 S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */
590 #elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
593 struct acl entries[NACLENTRIES];
598 count = acl ((char *) src_name, ACL_CNT, NACLENTRIES, NULL);
614 if (count > NACLENTRIES)
615 /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
618 if (acl ((char *) src_name, ACL_GET, count, entries) == count)
620 /* Huh? The number of ACL entries changed since the last call.
625 return qset_acl (dst_name, dest_desc, mode);
627 ret = acl ((char *) dst_name, ACL_SET, count, entries);
630 int saved_errno = errno;
634 if (!acl_nontrivial (count, entries))
635 return chmod_or_fchmod (dst_name, dest_desc, mode);
638 chmod_or_fchmod (dst_name, dest_desc, mode);
643 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
645 /* We did not call chmod so far, and either the mode and the ACL are
646 separate or special bits are to be set which don't fit into ACLs. */
648 return chmod_or_fchmod (dst_name, dest_desc, mode);
654 return qset_acl (dst_name, dest_desc, mode);
660 /* Copy access control lists from one file to another. If SOURCE_DESC is
661 a valid file descriptor, use file descriptor operations, else use
662 filename based operations on SRC_NAME. Likewise for DEST_DESC and
664 If access control lists are not available, fchmod the target file to
665 MODE. Also sets the non-permission bits of the destination file
666 (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
667 Return 0 if successful, otherwise output a diagnostic and return -1. */
670 copy_acl (const char *src_name, int source_desc, const char *dst_name,
671 int dest_desc, mode_t mode)
673 int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode);
677 error (0, errno, "%s", quote (src_name));
681 error (0, errno, _("preserving permissions for %s"), quote (dst_name));