1 /* copy-acl.c - copy access control list from one file to another file
3 Copyright (C) 2002-2003, 2005-2008 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 /* Copy access control lists from one file to another. If SOURCE_DESC is
28 a valid file descriptor, use file descriptor operations, else use
29 filename based operations on SRC_NAME. Likewise for DEST_DESC and
31 If access control lists are not available, fchmod the target file to
32 MODE. Also sets the non-permission bits of the destination file
33 (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
34 Return 0 if successful.
35 Return -2 and set errno for an error relating to the source file.
36 Return -1 and set errno for an error relating to the destination file. */
39 qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
40 int dest_desc, mode_t mode)
42 #if USE_ACL && HAVE_ACL_GET_FILE
43 /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
44 /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
46 /* Linux, FreeBSD, IRIX, Tru64 */
51 if (HAVE_ACL_GET_FD && source_desc != -1)
52 acl = acl_get_fd (source_desc);
54 acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
57 if (ACL_NOT_WELL_SUPPORTED (errno))
58 return qset_acl (dst_name, dest_desc, mode);
63 if (HAVE_ACL_SET_FD && dest_desc != -1)
64 ret = acl_set_fd (dest_desc, acl);
66 ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
69 int saved_errno = errno;
71 if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_access_nontrivial (acl))
74 return chmod_or_fchmod (dst_name, dest_desc, mode);
79 chmod_or_fchmod (dst_name, dest_desc, mode);
87 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
89 /* We did not call chmod so far, and either the mode and the ACL are
90 separate or special bits are to be set which don't fit into ACLs. */
92 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
98 acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
102 if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
104 int saved_errno = errno;
115 # else /* !MODE_INSIDE_ACL */
118 # if !HAVE_ACL_TYPE_EXTENDED
119 # error Must have ACL_TYPE_EXTENDED
122 /* On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
123 and acl_get_file (name, ACL_TYPE_DEFAULT)
124 always return NULL / EINVAL. You have to use
125 acl_get_file (name, ACL_TYPE_EXTENDED)
126 or acl_get_fd (open (name, ...))
129 acl_set_file (name, ACL_TYPE_ACCESS, acl)
130 and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
131 have the same effect as
132 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
133 Each of these calls sets the file's ACL. */
138 if (HAVE_ACL_GET_FD && source_desc != -1)
139 acl = acl_get_fd (source_desc);
141 acl = acl_get_file (src_name, ACL_TYPE_EXTENDED);
144 if (ACL_NOT_WELL_SUPPORTED (errno))
145 return qset_acl (dst_name, dest_desc, mode);
150 if (HAVE_ACL_SET_FD && dest_desc != -1)
151 ret = acl_set_fd (dest_desc, acl);
153 ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl);
156 int saved_errno = errno;
158 if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_extended_nontrivial (acl))
161 return chmod_or_fchmod (dst_name, dest_desc, mode);
166 chmod_or_fchmod (dst_name, dest_desc, mode);
174 /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
175 return chmod_or_fchmod (dst_name, dest_desc, mode);
179 #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
181 # if defined ACL_NO_TRIVIAL
182 /* Solaris 10 (newer version), which has additional API declared in
183 <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
184 acl_fromtext, ...). */
188 ret = (source_desc < 0
189 ? acl_get (src_name, ACL_NO_TRIVIAL, &aclp)
190 : facl_get (source_desc, ACL_NO_TRIVIAL, &aclp));
191 if (ret != 0 && errno != ENOSYS)
194 ret = qset_acl (dst_name, dest_desc, mode);
201 ? acl_set (dst_name, aclp)
202 : facl_set (dest_desc, aclp));
205 int saved_errno = errno;
216 # else /* Solaris, Cygwin, general case */
218 /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
219 of Unixware. The acl() call returns the access and default ACL both
232 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
233 file systems (whereas the other ones are used in UFS file systems).
235 pathconf (name, _PC_ACL_ENABLED)
236 fpathconf (desc, _PC_ACL_ENABLED)
237 that allows to determine which of the two kinds of ACLs is supported
238 for the given file. But some file systems may implement this call
239 incorrectly, so better not use it.
240 When fetching the source ACL, we simply fetch both ACL types.
241 When setting the destination ACL, we try either ACL types, assuming
242 that the kernel will translate the ACL from one form to the other.
243 (See in <http://docs.sun.com/app/docs/doc/819-2241/6n4huc7ia?l=en&a=view>
244 the description of ENOTSUP.) */
247 ace_count = (source_desc != -1
248 ? facl (source_desc, ACE_GETACLCNT, 0, NULL)
249 : acl (src_name, ACE_GETACLCNT, 0, NULL));
253 if (errno == ENOSYS || errno == EINVAL)
269 ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t));
270 if (ace_entries == NULL)
276 if ((source_desc != -1
277 ? facl (source_desc, ACE_GETACL, ace_count, ace_entries)
278 : acl (src_name, ACE_GETACL, ace_count, ace_entries))
281 /* Huh? The number of ACL entries changed since the last call.
288 count = (source_desc != -1
289 ? facl (source_desc, GETACLCNT, 0, NULL)
290 : acl (src_name, GETACLCNT, 0, NULL));
294 if (errno == ENOSYS || errno == ENOTSUP)
310 entries = (aclent_t *) malloc (count * sizeof (aclent_t));
317 if ((source_desc != -1
318 ? facl (source_desc, GETACL, count, entries)
319 : acl (src_name, GETACL, count, entries))
322 /* Huh? The number of ACL entries changed since the last call.
326 /* Is there an ACL of either kind? */
331 return qset_acl (dst_name, dest_desc, mode);
333 did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
334 saved_errno = 0; /* the first non-ignorable error code */
336 /* If both ace_entries and entries are available, try SETACL before
337 ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
342 ret = (dest_desc != -1
343 ? facl (dest_desc, SETACL, count, entries)
344 : acl (dst_name, SETACL, count, entries));
348 if (errno == ENOSYS && !acl_nontrivial (count, entries))
359 ret = (dest_desc != -1
360 ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries)
361 : acl (dst_name, ACE_SETACL, ace_count, ace_entries));
362 if (ret < 0 && saved_errno == 0)
365 if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
366 && !acl_ace_nontrivial (ace_count, ace_entries))
374 || did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
376 /* We did not call chmod so far, and either the mode and the ACL are
377 separate or special bits are to be set which don't fit into ACLs. */
379 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
381 if (saved_errno == 0)
395 #elif USE_ACL && HAVE_GETACL /* HP-UX */
398 struct acl_entry entries[NACLENTRIES];
403 count = (source_desc != -1
404 ? fgetacl (source_desc, 0, NULL)
405 : getacl (src_name, 0, NULL));
409 if (errno == ENOSYS || errno == EOPNOTSUPP)
421 if (count > NACLENTRIES)
422 /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
425 if ((source_desc != -1
426 ? fgetacl (source_desc, count, entries)
427 : getacl (src_name, count, entries))
430 /* Huh? The number of ACL entries changed since the last call.
435 return qset_acl (dst_name, dest_desc, mode);
437 ret = (dest_desc != -1
438 ? fsetacl (dest_desc, count, entries)
439 : setacl (dst_name, count, entries));
442 int saved_errno = errno;
444 if (errno == ENOSYS || errno == EOPNOTSUPP)
446 struct stat source_statbuf;
448 if ((source_desc != -1
449 ? fstat (source_desc, &source_statbuf)
450 : stat (src_name, &source_statbuf)) == 0)
452 if (!acl_nontrivial (count, entries, &source_statbuf))
453 return chmod_or_fchmod (dst_name, dest_desc, mode);
459 chmod_or_fchmod (dst_name, dest_desc, mode);
464 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
466 /* We did not call chmod so far, and either the mode and the ACL are
467 separate or special bits are to be set which don't fit into ACLs. */
469 return chmod_or_fchmod (dst_name, dest_desc, mode);
473 #elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
477 #elif USE_ACL && HAVE_STATACL /* older AIX */
479 union { struct acl a; char room[4096]; } u;
482 if ((source_desc != -1
483 ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u))
484 : statacl (src_name, STX_NORMAL, &u.a, sizeof (u)))
488 ret = (dest_desc != -1
489 ? fchacl (dest_desc, &u.a, u.a.acl_len)
490 : chacl (dst_name, &u.a, u.a.acl_len));
493 int saved_errno = errno;
495 chmod_or_fchmod (dst_name, dest_desc, mode);
500 /* No need to call chmod_or_fchmod at this point, since the mode bits
501 S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */
507 return qset_acl (dst_name, dest_desc, mode);
513 /* Copy access control lists from one file to another. If SOURCE_DESC is
514 a valid file descriptor, use file descriptor operations, else use
515 filename based operations on SRC_NAME. Likewise for DEST_DESC and
517 If access control lists are not available, fchmod the target file to
518 MODE. Also sets the non-permission bits of the destination file
519 (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
520 Return 0 if successful, otherwise output a diagnostic and return -1. */
523 copy_acl (const char *src_name, int source_desc, const char *dst_name,
524 int dest_desc, mode_t mode)
526 int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode);
530 error (0, errno, "%s", quote (src_name));
534 error (0, errno, _("preserving permissions for %s"), quote (dst_name));