acl: Export qcopy_acl.
[gnulib.git] / lib / copy-acl.c
1 /* copy-acl.c - copy access control list from one file to another file
2
3    Copyright (C) 2002-2003, 2005-2012 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 Grünbacher, 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 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 !HAVE_ACL_TYPE_EXTENDED
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_INSIDE_ACL || (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 /* HAVE_ACL_TYPE_EXTENDED */
119   /* MacOS X */
120
121   /* On MacOS X,  acl_get_file (name, ACL_TYPE_ACCESS)
122      and          acl_get_file (name, ACL_TYPE_DEFAULT)
123      always return NULL / EINVAL.  You have to use
124                   acl_get_file (name, ACL_TYPE_EXTENDED)
125      or           acl_get_fd (open (name, ...))
126      to retrieve an ACL.
127      On the other hand,
128                   acl_set_file (name, ACL_TYPE_ACCESS, acl)
129      and          acl_set_file (name, ACL_TYPE_DEFAULT, acl)
130      have the same effect as
131                   acl_set_file (name, ACL_TYPE_EXTENDED, acl):
132      Each of these calls sets the file's ACL.  */
133
134   acl_t acl;
135   int ret;
136
137   if (HAVE_ACL_GET_FD && source_desc != -1)
138     acl = acl_get_fd (source_desc);
139   else
140     acl = acl_get_file (src_name, ACL_TYPE_EXTENDED);
141   if (acl == NULL)
142     {
143       if (ACL_NOT_WELL_SUPPORTED (errno))
144         return qset_acl (dst_name, dest_desc, mode);
145       else
146         return -2;
147     }
148
149   if (HAVE_ACL_SET_FD && dest_desc != -1)
150     ret = acl_set_fd (dest_desc, acl);
151   else
152     ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl);
153   if (ret != 0)
154     {
155       int saved_errno = errno;
156
157       if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_extended_nontrivial (acl))
158         {
159           acl_free (acl);
160           return chmod_or_fchmod (dst_name, dest_desc, mode);
161         }
162       else
163         {
164           acl_free (acl);
165           chmod_or_fchmod (dst_name, dest_desc, mode);
166           errno = saved_errno;
167           return -1;
168         }
169     }
170   else
171     acl_free (acl);
172
173   /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly.  */
174   return chmod_or_fchmod (dst_name, dest_desc, mode);
175
176 # endif
177
178 #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
179
180   /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
181      of Unixware.  The acl() call returns the access and default ACL both
182      at once.  */
183 # ifdef ACE_GETACL
184   int ace_count;
185   ace_t *ace_entries;
186 # endif
187   int count;
188   aclent_t *entries;
189   int did_chmod;
190   int saved_errno;
191   int ret;
192
193 # ifdef ACE_GETACL
194   /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
195      file systems (whereas the other ones are used in UFS file systems).
196      There is an API
197        pathconf (name, _PC_ACL_ENABLED)
198        fpathconf (desc, _PC_ACL_ENABLED)
199      that allows to determine which of the two kinds of ACLs is supported
200      for the given file.  But some file systems may implement this call
201      incorrectly, so better not use it.
202      When fetching the source ACL, we simply fetch both ACL types.
203      When setting the destination ACL, we try either ACL types, assuming
204      that the kernel will translate the ACL from one form to the other.
205      (See in <http://docs.sun.com/app/docs/doc/819-2241/6n4huc7ia?l=en&a=view>
206      the description of ENOTSUP.)  */
207   for (;;)
208     {
209       ace_count = (source_desc != -1
210                    ? facl (source_desc, ACE_GETACLCNT, 0, NULL)
211                    : acl (src_name, ACE_GETACLCNT, 0, NULL));
212
213       if (ace_count < 0)
214         {
215           if (errno == ENOSYS || errno == EINVAL)
216             {
217               ace_count = 0;
218               ace_entries = NULL;
219               break;
220             }
221           else
222             return -2;
223         }
224
225       if (ace_count == 0)
226         {
227           ace_entries = NULL;
228           break;
229         }
230
231       ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t));
232       if (ace_entries == NULL)
233         {
234           errno = ENOMEM;
235           return -2;
236         }
237
238       if ((source_desc != -1
239            ? facl (source_desc, ACE_GETACL, ace_count, ace_entries)
240            : acl (src_name, ACE_GETACL, ace_count, ace_entries))
241           == ace_count)
242         break;
243       /* Huh? The number of ACL entries changed since the last call.
244          Repeat.  */
245     }
246 # endif
247
248   for (;;)
249     {
250       count = (source_desc != -1
251                ? facl (source_desc, GETACLCNT, 0, NULL)
252                : acl (src_name, GETACLCNT, 0, NULL));
253
254       if (count < 0)
255         {
256           if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
257             {
258               count = 0;
259               entries = NULL;
260               break;
261             }
262           else
263             return -2;
264         }
265
266       if (count == 0)
267         {
268           entries = NULL;
269           break;
270         }
271
272       entries = (aclent_t *) malloc (count * sizeof (aclent_t));
273       if (entries == NULL)
274         {
275           errno = ENOMEM;
276           return -2;
277         }
278
279       if ((source_desc != -1
280            ? facl (source_desc, GETACL, count, entries)
281            : acl (src_name, GETACL, count, entries))
282           == count)
283         break;
284       /* Huh? The number of ACL entries changed since the last call.
285          Repeat.  */
286     }
287
288   /* Is there an ACL of either kind?  */
289 # ifdef ACE_GETACL
290   if (ace_count == 0)
291 # endif
292     if (count == 0)
293       return qset_acl (dst_name, dest_desc, mode);
294
295   did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
296   saved_errno = 0; /* the first non-ignorable error code */
297
298   if (!MODE_INSIDE_ACL)
299     {
300       /* On Cygwin, it is necessary to call chmod before acl, because
301          chmod can change the contents of the ACL (in ways that don't
302          change the allowed accesses, but still visible).  */
303       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
304         saved_errno = errno;
305       did_chmod = 1;
306     }
307
308   /* If both ace_entries and entries are available, try SETACL before
309      ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
310      can.  */
311
312   if (count > 0)
313     {
314       ret = (dest_desc != -1
315              ? facl (dest_desc, SETACL, count, entries)
316              : acl (dst_name, SETACL, count, entries));
317       if (ret < 0 && saved_errno == 0)
318         {
319           saved_errno = errno;
320           if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
321               && !acl_nontrivial (count, entries))
322             saved_errno = 0;
323         }
324       else
325         did_chmod = 1;
326     }
327   free (entries);
328
329 # ifdef ACE_GETACL
330   if (ace_count > 0)
331     {
332       ret = (dest_desc != -1
333              ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries)
334              : acl (dst_name, ACE_SETACL, ace_count, ace_entries));
335       if (ret < 0 && saved_errno == 0)
336         {
337           saved_errno = errno;
338           if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
339               && !acl_ace_nontrivial (ace_count, ace_entries))
340             saved_errno = 0;
341         }
342     }
343   free (ace_entries);
344 # endif
345
346   if (MODE_INSIDE_ACL
347       && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
348     {
349       /* We did not call chmod so far, and either the mode and the ACL are
350          separate or special bits are to be set which don't fit into ACLs.  */
351
352       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
353         {
354           if (saved_errno == 0)
355             saved_errno = errno;
356         }
357     }
358
359   if (saved_errno)
360     {
361       errno = saved_errno;
362       return -1;
363     }
364   return 0;
365
366 #elif USE_ACL && HAVE_GETACL /* HP-UX */
367
368   int count;
369   struct acl_entry entries[NACLENTRIES];
370 # if HAVE_ACLV_H
371   int aclv_count;
372   struct acl aclv_entries[NACLVENTRIES];
373 # endif
374   int did_chmod;
375   int saved_errno;
376   int ret;
377
378   for (;;)
379     {
380       count = (source_desc != -1
381                ? fgetacl (source_desc, 0, NULL)
382                : getacl (src_name, 0, NULL));
383
384       if (count < 0)
385         {
386           if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
387             {
388               count = 0;
389               break;
390             }
391           else
392             return -2;
393         }
394
395       if (count == 0)
396         break;
397
398       if (count > NACLENTRIES)
399         /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
400         abort ();
401
402       if ((source_desc != -1
403            ? fgetacl (source_desc, count, entries)
404            : getacl (src_name, count, entries))
405           == count)
406         break;
407       /* Huh? The number of ACL entries changed since the last call.
408          Repeat.  */
409     }
410
411 # if HAVE_ACLV_H
412   for (;;)
413     {
414       aclv_count = acl ((char *) src_name, ACL_CNT, NACLVENTRIES, aclv_entries);
415
416       if (aclv_count < 0)
417         {
418           if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
419             {
420               count = 0;
421               break;
422             }
423           else
424             return -2;
425         }
426
427       if (aclv_count == 0)
428         break;
429
430       if (aclv_count > NACLVENTRIES)
431         /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation.  */
432         abort ();
433
434       if (acl ((char *) src_name, ACL_GET, aclv_count, aclv_entries)
435           == aclv_count)
436         break;
437       /* Huh? The number of ACL entries changed since the last call.
438          Repeat.  */
439     }
440 # endif
441
442   if (count == 0)
443 # if HAVE_ACLV_H
444     if (aclv_count == 0)
445 # endif
446       return qset_acl (dst_name, dest_desc, mode);
447
448   did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
449   saved_errno = 0; /* the first non-ignorable error code */
450
451   if (count > 0)
452     {
453       ret = (dest_desc != -1
454              ? fsetacl (dest_desc, count, entries)
455              : setacl (dst_name, count, entries));
456       if (ret < 0 && saved_errno == 0)
457         {
458           saved_errno = errno;
459           if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
460             {
461               struct stat source_statbuf;
462
463               if ((source_desc != -1
464                    ? fstat (source_desc, &source_statbuf)
465                    : stat (src_name, &source_statbuf)) == 0)
466                 {
467                   if (!acl_nontrivial (count, entries, &source_statbuf))
468                     saved_errno = 0;
469                 }
470               else
471                 saved_errno = errno;
472             }
473         }
474       else
475         did_chmod = 1;
476     }
477
478 # if HAVE_ACLV_H
479   if (aclv_count > 0)
480     {
481       ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries);
482       if (ret < 0 && saved_errno == 0)
483         {
484           saved_errno = errno;
485           if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
486             {
487               if (!aclv_nontrivial (aclv_count, aclv_entries))
488                 saved_errno = 0;
489             }
490         }
491       else
492         did_chmod = 1;
493     }
494 # endif
495
496   if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
497     {
498       /* We did not call chmod so far, and special bits are to be set which
499          don't fit into ACLs.  */
500
501       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
502         {
503           if (saved_errno == 0)
504             saved_errno = errno;
505         }
506     }
507
508   if (saved_errno)
509     {
510       errno = saved_errno;
511       return -1;
512     }
513   return 0;
514
515 #elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
516
517   /* TODO */
518
519 #elif USE_ACL && HAVE_STATACL /* older AIX */
520
521   union { struct acl a; char room[4096]; } u;
522   int ret;
523
524   if ((source_desc != -1
525        ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u))
526        : statacl (src_name, STX_NORMAL, &u.a, sizeof (u)))
527       < 0)
528     return -2;
529
530   ret = (dest_desc != -1
531          ? fchacl (dest_desc, &u.a, u.a.acl_len)
532          : chacl (dst_name, &u.a, u.a.acl_len));
533   if (ret < 0)
534     {
535       int saved_errno = errno;
536
537       chmod_or_fchmod (dst_name, dest_desc, mode);
538       errno = saved_errno;
539       return -1;
540     }
541
542   /* No need to call chmod_or_fchmod at this point, since the mode bits
543      S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL.  */
544
545   return 0;
546
547 #elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
548
549   int count;
550   struct acl entries[NACLENTRIES];
551   int ret;
552
553   for (;;)
554     {
555       count = acl ((char *) src_name, ACL_CNT, NACLENTRIES, NULL);
556
557       if (count < 0)
558         {
559           if (0)
560             {
561               count = 0;
562               break;
563             }
564           else
565             return -2;
566         }
567
568       if (count == 0)
569         break;
570
571       if (count > NACLENTRIES)
572         /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
573         abort ();
574
575       if (acl ((char *) src_name, ACL_GET, count, entries) == count)
576         break;
577       /* Huh? The number of ACL entries changed since the last call.
578          Repeat.  */
579     }
580
581   if (count == 0)
582     return qset_acl (dst_name, dest_desc, mode);
583
584   ret = acl ((char *) dst_name, ACL_SET, count, entries);
585   if (ret < 0)
586     {
587       int saved_errno = errno;
588
589       if (0)
590         {
591           if (!acl_nontrivial (count, entries))
592             return chmod_or_fchmod (dst_name, dest_desc, mode);
593         }
594
595       chmod_or_fchmod (dst_name, dest_desc, mode);
596       errno = saved_errno;
597       return -1;
598     }
599
600   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
601     {
602       /* We did not call chmod so far, and either the mode and the ACL are
603          separate or special bits are to be set which don't fit into ACLs.  */
604
605       return chmod_or_fchmod (dst_name, dest_desc, mode);
606     }
607   return 0;
608
609 #else
610
611   return qset_acl (dst_name, dest_desc, mode);
612
613 #endif
614 }
615
616
617 /* Copy access control lists from one file to another. If SOURCE_DESC is
618    a valid file descriptor, use file descriptor operations, else use
619    filename based operations on SRC_NAME. Likewise for DEST_DESC and
620    DST_NAME.
621    If access control lists are not available, fchmod the target file to
622    MODE.  Also sets the non-permission bits of the destination file
623    (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
624    Return 0 if successful, otherwise output a diagnostic and return a
625    negative error code.  */
626
627 int
628 copy_acl (const char *src_name, int source_desc, const char *dst_name,
629           int dest_desc, mode_t mode)
630 {
631   int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode);
632   switch (ret)
633     {
634     case -2:
635       error (0, errno, "%s", quote (src_name));
636       break;
637
638     case -1:
639       error (0, errno, _("preserving permissions for %s"), quote (dst_name));
640       break;
641
642     default:
643       break;
644     }
645   return ret;
646 }