lisp/gnus/mm-decode.el: Fix previous commit
[emacs.git] / lib / qcopy-acl.c
blobaac76a124326cfa4a79a8dabfd8965e883f7cc4c
1 /* copy-acl.c - copy access control list from one file to another file
3 Copyright (C) 2002-2003, 2005-2015 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. */
20 #include <config.h>
22 #include "acl.h"
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
30 DST_NAME.
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. */
38 int
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, Mac OS X, IRIX, Tru64 */
45 # if !HAVE_ACL_TYPE_EXTENDED
46 /* Linux, FreeBSD, IRIX, Tru64 */
48 acl_t acl;
49 int ret;
51 if (HAVE_ACL_GET_FD && source_desc != -1)
52 acl = acl_get_fd (source_desc);
53 else
54 acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
55 if (acl == NULL)
57 if (! acl_errno_valid (errno))
58 return qset_acl (dst_name, dest_desc, mode);
59 else
60 return -2;
63 if (HAVE_ACL_SET_FD && dest_desc != -1)
64 ret = acl_set_fd (dest_desc, acl);
65 else
66 ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
67 if (ret != 0)
69 int saved_errno = errno;
71 if (! acl_errno_valid (errno) && !acl_access_nontrivial (acl))
73 acl_free (acl);
74 return chmod_or_fchmod (dst_name, dest_desc, mode);
76 else
78 acl_free (acl);
79 chmod_or_fchmod (dst_name, dest_desc, mode);
80 errno = saved_errno;
81 return -1;
84 else
85 acl_free (acl);
87 if (!MODE_INSIDE_ACL || (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)
93 return -1;
96 if (S_ISDIR (mode))
98 acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
99 if (acl == NULL)
100 return -2;
102 if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
104 int saved_errno = errno;
106 acl_free (acl);
107 errno = saved_errno;
108 return -1;
110 else
111 acl_free (acl);
113 return 0;
115 # else /* HAVE_ACL_TYPE_EXTENDED */
116 /* Mac OS X */
118 /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
119 and acl_get_file (name, ACL_TYPE_DEFAULT)
120 always return NULL / EINVAL. You have to use
121 acl_get_file (name, ACL_TYPE_EXTENDED)
122 or acl_get_fd (open (name, ...))
123 to retrieve an ACL.
124 On the other hand,
125 acl_set_file (name, ACL_TYPE_ACCESS, acl)
126 and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
127 have the same effect as
128 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
129 Each of these calls sets the file's ACL. */
131 acl_t acl;
132 int ret;
134 if (HAVE_ACL_GET_FD && source_desc != -1)
135 acl = acl_get_fd (source_desc);
136 else
137 acl = acl_get_file (src_name, ACL_TYPE_EXTENDED);
138 if (acl == NULL)
140 if (!acl_errno_valid (errno))
141 return qset_acl (dst_name, dest_desc, mode);
142 else
143 return -2;
146 if (HAVE_ACL_SET_FD && dest_desc != -1)
147 ret = acl_set_fd (dest_desc, acl);
148 else
149 ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl);
150 if (ret != 0)
152 int saved_errno = errno;
154 if (!acl_errno_valid (saved_errno) && !acl_extended_nontrivial (acl))
156 acl_free (acl);
157 return chmod_or_fchmod (dst_name, dest_desc, mode);
159 else
161 acl_free (acl);
162 chmod_or_fchmod (dst_name, dest_desc, mode);
163 errno = saved_errno;
164 return -1;
167 else
168 acl_free (acl);
170 /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
171 return chmod_or_fchmod (dst_name, dest_desc, mode);
173 # endif
175 #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
177 /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
178 of Unixware. The acl() call returns the access and default ACL both
179 at once. */
180 # ifdef ACE_GETACL
181 int ace_count;
182 ace_t *ace_entries;
183 # endif
184 int count;
185 aclent_t *entries;
186 int did_chmod;
187 int saved_errno;
188 int ret;
190 # ifdef ACE_GETACL
191 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
192 file systems (whereas the other ones are used in UFS file systems).
193 There is an API
194 pathconf (name, _PC_ACL_ENABLED)
195 fpathconf (desc, _PC_ACL_ENABLED)
196 that allows to determine which of the two kinds of ACLs is supported
197 for the given file. But some file systems may implement this call
198 incorrectly, so better not use it.
199 When fetching the source ACL, we simply fetch both ACL types.
200 When setting the destination ACL, we try either ACL types, assuming
201 that the kernel will translate the ACL from one form to the other.
202 (See in <http://docs.sun.com/app/docs/doc/819-2241/6n4huc7ia?l=en&a=view>
203 the description of ENOTSUP.) */
204 for (;;)
206 ace_count = (source_desc != -1
207 ? facl (source_desc, ACE_GETACLCNT, 0, NULL)
208 : acl (src_name, ACE_GETACLCNT, 0, NULL));
210 if (ace_count < 0)
212 if (errno == ENOSYS || errno == EINVAL)
214 ace_count = 0;
215 ace_entries = NULL;
216 break;
218 else
219 return -2;
222 if (ace_count == 0)
224 ace_entries = NULL;
225 break;
228 ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t));
229 if (ace_entries == NULL)
231 errno = ENOMEM;
232 return -2;
235 ret = (source_desc != -1
236 ? facl (source_desc, ACE_GETACL, ace_count, ace_entries)
237 : acl (src_name, ACE_GETACL, ace_count, ace_entries));
238 if (ret < 0)
240 free (ace_entries);
241 if (errno == ENOSYS || errno == EINVAL)
243 ace_count = 0;
244 ace_entries = NULL;
245 break;
247 else
248 return -2;
250 if (ret == ace_count)
251 break;
252 /* Huh? The number of ACL entries changed since the last call.
253 Repeat. */
255 # endif
257 for (;;)
259 count = (source_desc != -1
260 ? facl (source_desc, GETACLCNT, 0, NULL)
261 : acl (src_name, GETACLCNT, 0, NULL));
263 if (count < 0)
265 if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
267 count = 0;
268 entries = NULL;
269 break;
271 else
272 return -2;
275 if (count == 0)
277 entries = NULL;
278 break;
281 entries = (aclent_t *) malloc (count * sizeof (aclent_t));
282 if (entries == NULL)
284 errno = ENOMEM;
285 return -2;
288 if ((source_desc != -1
289 ? facl (source_desc, GETACL, count, entries)
290 : acl (src_name, GETACL, count, entries))
291 == count)
292 break;
293 /* Huh? The number of ACL entries changed since the last call.
294 Repeat. */
297 /* Is there an ACL of either kind? */
298 # ifdef ACE_GETACL
299 if (ace_count == 0)
300 # endif
301 if (count == 0)
302 return qset_acl (dst_name, dest_desc, mode);
304 did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
305 saved_errno = 0; /* the first non-ignorable error code */
307 if (!MODE_INSIDE_ACL)
309 /* On Cygwin, it is necessary to call chmod before acl, because
310 chmod can change the contents of the ACL (in ways that don't
311 change the allowed accesses, but still visible). */
312 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
313 saved_errno = errno;
314 did_chmod = 1;
317 /* If both ace_entries and entries are available, try SETACL before
318 ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
319 can. */
321 if (count > 0)
323 ret = (dest_desc != -1
324 ? facl (dest_desc, SETACL, count, entries)
325 : acl (dst_name, SETACL, count, entries));
326 if (ret < 0 && saved_errno == 0)
328 saved_errno = errno;
329 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
330 && !acl_nontrivial (count, entries))
331 saved_errno = 0;
333 else
334 did_chmod = 1;
336 free (entries);
338 # ifdef ACE_GETACL
339 if (ace_count > 0)
341 ret = (dest_desc != -1
342 ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries)
343 : acl (dst_name, ACE_SETACL, ace_count, ace_entries));
344 if (ret < 0 && saved_errno == 0)
346 saved_errno = errno;
347 if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
348 && !acl_ace_nontrivial (ace_count, ace_entries))
349 saved_errno = 0;
352 free (ace_entries);
353 # endif
355 if (MODE_INSIDE_ACL
356 && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
358 /* We did not call chmod so far, and either the mode and the ACL are
359 separate or special bits are to be set which don't fit into ACLs. */
361 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
363 if (saved_errno == 0)
364 saved_errno = errno;
368 if (saved_errno)
370 errno = saved_errno;
371 return -1;
373 return 0;
375 #elif USE_ACL && HAVE_GETACL /* HP-UX */
377 struct acl_entry entries[NACLENTRIES];
378 int count;
379 # if HAVE_ACLV_H
380 struct acl aclv_entries[NACLVENTRIES];
381 int aclv_count;
382 # endif
383 int did_chmod;
384 int saved_errno;
385 int ret;
387 count = (source_desc != -1
388 ? fgetacl (source_desc, NACLENTRIES, entries)
389 : getacl (src_name, NACLENTRIES, entries));
391 if (count < 0)
393 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
394 count = 0;
395 else
396 return -2;
398 else if (count > 0)
400 if (count > NACLENTRIES)
401 /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
402 abort ();
405 # if HAVE_ACLV_H
406 aclv_count = acl ((char *) src_name, ACL_GET, NACLVENTRIES, aclv_entries);
408 if (aclv_count < 0)
410 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
411 count = 0;
412 else
413 return -2;
415 else if (aclv_count > 0)
417 if (aclv_count > NACLVENTRIES)
418 /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation. */
419 abort ();
421 # endif
423 if (count == 0)
424 # if HAVE_ACLV_H
425 if (aclv_count == 0)
426 # endif
427 return qset_acl (dst_name, dest_desc, mode);
429 did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
430 saved_errno = 0; /* the first non-ignorable error code */
432 if (count > 0)
434 ret = (dest_desc != -1
435 ? fsetacl (dest_desc, count, entries)
436 : setacl (dst_name, count, entries));
437 if (ret < 0 && saved_errno == 0)
439 saved_errno = errno;
440 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
442 struct stat source_statbuf;
444 if ((source_desc != -1
445 ? fstat (source_desc, &source_statbuf)
446 : stat (src_name, &source_statbuf)) == 0)
448 if (!acl_nontrivial (count, entries, &source_statbuf))
449 saved_errno = 0;
451 else
452 saved_errno = errno;
455 else
456 did_chmod = 1;
459 # if HAVE_ACLV_H
460 if (aclv_count > 0)
462 ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries);
463 if (ret < 0 && saved_errno == 0)
465 saved_errno = errno;
466 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
468 if (!aclv_nontrivial (aclv_count, aclv_entries))
469 saved_errno = 0;
472 else
473 did_chmod = 1;
475 # endif
477 if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
479 /* We did not call chmod so far, and special bits are to be set which
480 don't fit into ACLs. */
482 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
484 if (saved_errno == 0)
485 saved_errno = errno;
489 if (saved_errno)
491 errno = saved_errno;
492 return -1;
494 return 0;
496 #elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
498 /* TODO */
500 #elif USE_ACL && HAVE_STATACL /* older AIX */
502 union { struct acl a; char room[4096]; } u;
503 int ret;
505 if ((source_desc != -1
506 ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u))
507 : statacl (src_name, STX_NORMAL, &u.a, sizeof (u)))
508 < 0)
509 return -2;
511 ret = (dest_desc != -1
512 ? fchacl (dest_desc, &u.a, u.a.acl_len)
513 : chacl (dst_name, &u.a, u.a.acl_len));
514 if (ret < 0)
516 int saved_errno = errno;
518 chmod_or_fchmod (dst_name, dest_desc, mode);
519 errno = saved_errno;
520 return -1;
523 /* No need to call chmod_or_fchmod at this point, since the mode bits
524 S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */
526 return 0;
528 #elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
530 struct acl entries[NACLENTRIES];
531 int count;
532 int ret;
534 count = acl ((char *) src_name, ACL_GET, NACLENTRIES, entries);
536 if (count < 0)
538 if (0)
539 count = 0;
540 else
541 return -2;
543 else if (count > 0)
545 if (count > NACLENTRIES)
546 /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
547 abort ();
550 if (count == 0)
551 return qset_acl (dst_name, dest_desc, mode);
553 ret = acl ((char *) dst_name, ACL_SET, count, entries);
554 if (ret < 0)
556 int saved_errno = errno;
558 if (0)
560 if (!acl_nontrivial (count, entries))
561 return chmod_or_fchmod (dst_name, dest_desc, mode);
564 chmod_or_fchmod (dst_name, dest_desc, mode);
565 errno = saved_errno;
566 return -1;
569 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
571 /* We did not call chmod so far, and either the mode and the ACL are
572 separate or special bits are to be set which don't fit into ACLs. */
574 return chmod_or_fchmod (dst_name, dest_desc, mode);
576 return 0;
578 #else
580 return qset_acl (dst_name, dest_desc, mode);
582 #endif