Merge commit 'a39d4083cab589d7cd6a13e8a4b8db8875261d75'
[gnulib.git] / lib / qcopy-acl.c
1 /* copy-acl.c - copy access control list from one file to another file
2
3    Copyright (C) 2002-2003, 2005-2014 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
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.  */
37
38 int
39 qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
40            int dest_desc, mode_t mode)
41 {
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 */
47
48   acl_t acl;
49   int ret;
50
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)
56     {
57       if (! acl_errno_valid (errno))
58         return qset_acl (dst_name, dest_desc, mode);
59       else
60         return -2;
61     }
62
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)
68     {
69       int saved_errno = errno;
70
71       if (! acl_errno_valid (errno) && !acl_access_nontrivial (acl))
72         {
73           acl_free (acl);
74           return chmod_or_fchmod (dst_name, dest_desc, mode);
75         }
76       else
77         {
78           acl_free (acl);
79           chmod_or_fchmod (dst_name, dest_desc, mode);
80           errno = saved_errno;
81           return -1;
82         }
83     }
84   else
85     acl_free (acl);
86
87   if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
88     {
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.  */
91
92       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
93         return -1;
94     }
95
96   if (S_ISDIR (mode))
97     {
98       acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
99       if (acl == NULL)
100         return -2;
101
102       if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
103         {
104           int saved_errno = errno;
105
106           acl_free (acl);
107           errno = saved_errno;
108           return -1;
109         }
110       else
111         acl_free (acl);
112     }
113   return 0;
114
115 # else /* HAVE_ACL_TYPE_EXTENDED */
116   /* Mac OS X */
117
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.  */
130
131   acl_t acl;
132   int ret;
133
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)
139     {
140       if (!acl_errno_valid (errno))
141         return qset_acl (dst_name, dest_desc, mode);
142       else
143         return -2;
144     }
145
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)
151     {
152       int saved_errno = errno;
153
154       if (!acl_errno_valid (saved_errno) && !acl_extended_nontrivial (acl))
155         {
156           acl_free (acl);
157           return chmod_or_fchmod (dst_name, dest_desc, mode);
158         }
159       else
160         {
161           acl_free (acl);
162           chmod_or_fchmod (dst_name, dest_desc, mode);
163           errno = saved_errno;
164           return -1;
165         }
166     }
167   else
168     acl_free (acl);
169
170   /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly.  */
171   return chmod_or_fchmod (dst_name, dest_desc, mode);
172
173 # endif
174
175 #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
176
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;
189
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 (;;)
205     {
206       ace_count = (source_desc != -1
207                    ? facl (source_desc, ACE_GETACLCNT, 0, NULL)
208                    : acl (src_name, ACE_GETACLCNT, 0, NULL));
209
210       if (ace_count < 0)
211         {
212           if (errno == ENOSYS || errno == EINVAL)
213             {
214               ace_count = 0;
215               ace_entries = NULL;
216               break;
217             }
218           else
219             return -2;
220         }
221
222       if (ace_count == 0)
223         {
224           ace_entries = NULL;
225           break;
226         }
227
228       ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t));
229       if (ace_entries == NULL)
230         {
231           errno = ENOMEM;
232           return -2;
233         }
234
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)
239         {
240           free (ace_entries);
241           if (errno == ENOSYS || errno == EINVAL)
242             {
243               ace_count = 0;
244               ace_entries = NULL;
245               break;
246             }
247           else
248             return -2;
249         }
250       if (ret == ace_count)
251         break;
252       /* Huh? The number of ACL entries changed since the last call.
253          Repeat.  */
254     }
255 # endif
256
257   for (;;)
258     {
259       count = (source_desc != -1
260                ? facl (source_desc, GETACLCNT, 0, NULL)
261                : acl (src_name, GETACLCNT, 0, NULL));
262
263       if (count < 0)
264         {
265           if (errno == ENOSYS || errno == ENOTSUP || errno == EOPNOTSUPP)
266             {
267               count = 0;
268               entries = NULL;
269               break;
270             }
271           else
272             return -2;
273         }
274
275       if (count == 0)
276         {
277           entries = NULL;
278           break;
279         }
280
281       entries = (aclent_t *) malloc (count * sizeof (aclent_t));
282       if (entries == NULL)
283         {
284           errno = ENOMEM;
285           return -2;
286         }
287
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.  */
295     }
296
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);
303
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 */
306
307   if (!MODE_INSIDE_ACL)
308     {
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;
315     }
316
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.  */
320
321   if (count > 0)
322     {
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)
327         {
328           saved_errno = errno;
329           if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
330               && !acl_nontrivial (count, entries))
331             saved_errno = 0;
332         }
333       else
334         did_chmod = 1;
335     }
336   free (entries);
337
338 # ifdef ACE_GETACL
339   if (ace_count > 0)
340     {
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)
345         {
346           saved_errno = errno;
347           if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
348               && !acl_ace_nontrivial (ace_count, ace_entries))
349             saved_errno = 0;
350         }
351     }
352   free (ace_entries);
353 # endif
354
355   if (MODE_INSIDE_ACL
356       && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
357     {
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.  */
360
361       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
362         {
363           if (saved_errno == 0)
364             saved_errno = errno;
365         }
366     }
367
368   if (saved_errno)
369     {
370       errno = saved_errno;
371       return -1;
372     }
373   return 0;
374
375 #elif USE_ACL && HAVE_GETACL /* HP-UX */
376
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;
386
387   count = (source_desc != -1
388            ? fgetacl (source_desc, NACLENTRIES, entries)
389            : getacl (src_name, NACLENTRIES, entries));
390
391   if (count < 0)
392     {
393       if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
394         count = 0;
395       else
396         return -2;
397     }
398   else if (count > 0)
399     {
400       if (count > NACLENTRIES)
401         /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
402         abort ();
403     }
404
405 # if HAVE_ACLV_H
406   aclv_count = acl ((char *) src_name, ACL_GET, NACLVENTRIES, aclv_entries);
407
408   if (aclv_count < 0)
409     {
410       if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
411         count = 0;
412       else
413         return -2;
414     }
415   else if (aclv_count > 0)
416     {
417       if (aclv_count > NACLVENTRIES)
418         /* If NACLVENTRIES cannot be trusted, use dynamic memory allocation.  */
419         abort ();
420     }
421 # endif
422
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);
428
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 */
431
432   if (count > 0)
433     {
434       ret = (dest_desc != -1
435              ? fsetacl (dest_desc, count, entries)
436              : setacl (dst_name, count, entries));
437       if (ret < 0 && saved_errno == 0)
438         {
439           saved_errno = errno;
440           if (errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
441             {
442               struct stat source_statbuf;
443
444               if ((source_desc != -1
445                    ? fstat (source_desc, &source_statbuf)
446                    : stat (src_name, &source_statbuf)) == 0)
447                 {
448                   if (!acl_nontrivial (count, entries, &source_statbuf))
449                     saved_errno = 0;
450                 }
451               else
452                 saved_errno = errno;
453             }
454         }
455       else
456         did_chmod = 1;
457     }
458
459 # if HAVE_ACLV_H
460   if (aclv_count > 0)
461     {
462       ret = acl ((char *) dst_name, ACL_SET, aclv_count, aclv_entries);
463       if (ret < 0 && saved_errno == 0)
464         {
465           saved_errno = errno;
466           if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
467             {
468               if (!aclv_nontrivial (aclv_count, aclv_entries))
469                 saved_errno = 0;
470             }
471         }
472       else
473         did_chmod = 1;
474     }
475 # endif
476
477   if (did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
478     {
479       /* We did not call chmod so far, and special bits are to be set which
480          don't fit into ACLs.  */
481
482       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
483         {
484           if (saved_errno == 0)
485             saved_errno = errno;
486         }
487     }
488
489   if (saved_errno)
490     {
491       errno = saved_errno;
492       return -1;
493     }
494   return 0;
495
496 #elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
497
498   /* TODO */
499
500 #elif USE_ACL && HAVE_STATACL /* older AIX */
501
502   union { struct acl a; char room[4096]; } u;
503   int ret;
504
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;
510
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)
515     {
516       int saved_errno = errno;
517
518       chmod_or_fchmod (dst_name, dest_desc, mode);
519       errno = saved_errno;
520       return -1;
521     }
522
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.  */
525
526   return 0;
527
528 #elif USE_ACL && HAVE_ACLSORT /* NonStop Kernel */
529
530   struct acl entries[NACLENTRIES];
531   int count;
532   int ret;
533
534   count = acl ((char *) src_name, ACL_GET, NACLENTRIES, entries);
535
536   if (count < 0)
537     {
538       if (0)
539         count = 0;
540       else
541         return -2;
542     }
543   else if (count > 0)
544     {
545       if (count > NACLENTRIES)
546         /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
547         abort ();
548     }
549
550   if (count == 0)
551     return qset_acl (dst_name, dest_desc, mode);
552
553   ret = acl ((char *) dst_name, ACL_SET, count, entries);
554   if (ret < 0)
555     {
556       int saved_errno = errno;
557
558       if (0)
559         {
560           if (!acl_nontrivial (count, entries))
561             return chmod_or_fchmod (dst_name, dest_desc, mode);
562         }
563
564       chmod_or_fchmod (dst_name, dest_desc, mode);
565       errno = saved_errno;
566       return -1;
567     }
568
569   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
570     {
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.  */
573
574       return chmod_or_fchmod (dst_name, dest_desc, mode);
575     }
576   return 0;
577
578 #else
579
580   return qset_acl (dst_name, dest_desc, mode);
581
582 #endif
583 }