Add me as co-author.
[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-2008 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 static 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, MacOS X, IRIX, Tru64 */
45 # if MODE_INSIDE_ACL
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_NOT_WELL_SUPPORTED (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_NOT_WELL_SUPPORTED (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 & (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 /* !MODE_INSIDE_ACL */
116   /* MacOS X */
117
118 #  if !HAVE_ACL_TYPE_EXTENDED
119 #   error Must have ACL_TYPE_EXTENDED
120 #  endif
121
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, ...))
127      to retrieve an ACL.
128      On the other hand,
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.  */
134
135   acl_t acl;
136   int ret;
137
138   if (HAVE_ACL_GET_FD && source_desc != -1)
139     acl = acl_get_fd (source_desc);
140   else
141     acl = acl_get_file (src_name, ACL_TYPE_EXTENDED);
142   if (acl == NULL)
143     {
144       if (ACL_NOT_WELL_SUPPORTED (errno))
145         return qset_acl (dst_name, dest_desc, mode);
146       else
147         return -2;
148     }
149
150   if (HAVE_ACL_SET_FD && dest_desc != -1)
151     ret = acl_set_fd (dest_desc, acl);
152   else
153     ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl);
154   if (ret != 0)
155     {
156       int saved_errno = errno;
157
158       if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_extended_nontrivial (acl))
159         {
160           acl_free (acl);
161           return chmod_or_fchmod (dst_name, dest_desc, mode);
162         }
163       else
164         {
165           acl_free (acl);
166           chmod_or_fchmod (dst_name, dest_desc, mode);
167           errno = saved_errno;
168           return -1;
169         }
170     }
171   else
172     acl_free (acl);
173
174   /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly.  */
175   return chmod_or_fchmod (dst_name, dest_desc, mode);
176
177 # endif
178
179 #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
180
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, ...).  */
185
186   int ret;
187   acl_t *aclp = NULL;
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)
192     return -2;
193
194   ret = qset_acl (dst_name, dest_desc, mode);
195   if (ret != 0)
196     return -1;
197
198   if (aclp)
199     {
200       ret = (dest_desc < 0
201              ? acl_set (dst_name, aclp)
202              : facl_set (dest_desc, aclp));
203       if (ret != 0)
204         {
205           int saved_errno = errno;
206
207           acl_free (aclp);
208           errno = saved_errno;
209           return -1;
210         }
211       acl_free (aclp);
212     }
213
214   return 0;
215
216 # else /* Solaris, Cygwin, general case */
217
218   /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
219      of Unixware.  The acl() call returns the access and default ACL both
220      at once.  */
221 #  ifdef ACE_GETACL
222   int ace_count;
223   ace_t *ace_entries;
224 #  endif
225   int count;
226   aclent_t *entries;
227   int did_chmod;
228   int saved_errno;
229   int ret;
230
231 #  ifdef ACE_GETACL
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).
234      There is an API
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.)  */
245   for (;;)
246     {
247       ace_count = (source_desc != -1
248                    ? facl (source_desc, ACE_GETACLCNT, 0, NULL)
249                    : acl (src_name, ACE_GETACLCNT, 0, NULL));
250
251       if (ace_count < 0)
252         {
253           if (errno == ENOSYS || errno == EINVAL)
254             {
255               ace_count = 0;
256               ace_entries = NULL;
257               break;
258             }
259           else
260             return -2;
261         }
262
263       if (ace_count == 0)
264         {
265           ace_entries = NULL;
266           break;
267         }
268
269       ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t));
270       if (ace_entries == NULL)
271         {
272           errno = ENOMEM;
273           return -2;
274         }
275
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))
279           == ace_count)
280         break;
281       /* Huh? The number of ACL entries changed since the last call.
282          Repeat.  */
283     }
284 #  endif
285
286   for (;;)
287     {
288       count = (source_desc != -1
289                ? facl (source_desc, GETACLCNT, 0, NULL)
290                : acl (src_name, GETACLCNT, 0, NULL));
291
292       if (count < 0)
293         {
294           if (errno == ENOSYS || errno == ENOTSUP)
295             {
296               count = 0;
297               entries = NULL;
298               break;
299             }
300           else
301             return -2;
302         }
303
304       if (count == 0)
305         {
306           entries = NULL;
307           break;
308         }
309
310       entries = (aclent_t *) malloc (count * sizeof (aclent_t));
311       if (entries == NULL)
312         {
313           errno = ENOMEM;
314           return -2;
315         }
316
317       if ((source_desc != -1
318            ? facl (source_desc, GETACL, count, entries)
319            : acl (src_name, GETACL, count, entries))
320           == count)
321         break;
322       /* Huh? The number of ACL entries changed since the last call.
323          Repeat.  */
324     }
325
326   /* Is there an ACL of either kind?  */
327 #  ifdef ACE_GETACL
328   if (ace_count == 0)
329 #  endif
330     if (count == 0)
331       return qset_acl (dst_name, dest_desc, mode);
332
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 */
335
336   /* If both ace_entries and entries are available, try SETACL before
337      ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
338      can.  */
339
340   if (count > 0)
341     {
342       ret = (dest_desc != -1
343              ? facl (dest_desc, SETACL, count, entries)
344              : acl (dst_name, SETACL, count, entries));
345       if (ret < 0)
346         {
347           saved_errno = errno;
348           if (errno == ENOSYS && !acl_nontrivial (count, entries))
349             saved_errno = 0;
350         }
351       else
352         did_chmod = 1;
353     }
354   free (entries);
355
356 #  ifdef ACE_GETACL
357   if (ace_count > 0)
358     {
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)
363         {
364           saved_errno = errno;
365           if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
366               && !acl_ace_nontrivial (ace_count, ace_entries))
367             saved_errno = 0;
368         }
369     }
370   free (ace_entries);
371 #  endif
372
373   if (!MODE_INSIDE_ACL
374       || did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
375     {
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.  */
378
379       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
380         {
381           if (saved_errno == 0)
382             saved_errno = errno;
383         }
384     }
385
386   if (saved_errno)
387     {
388       errno = saved_errno;
389       return -1;
390     }
391   return 0;
392
393 # endif
394
395 #elif USE_ACL && HAVE_GETACL /* HP-UX */
396
397   int count;
398   struct acl_entry entries[NACLENTRIES];
399   int ret;
400
401   for (;;)
402     {
403       count = (source_desc != -1
404                ? fgetacl (source_desc, 0, NULL)
405                : getacl (src_name, 0, NULL));
406
407       if (count < 0)
408         {
409           if (errno == ENOSYS || errno == EOPNOTSUPP)
410             {
411               count = 0;
412               break;
413             }
414           else
415             return -2;
416         }
417
418       if (count == 0)
419         break;
420
421       if (count > NACLENTRIES)
422         /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
423         abort ();
424
425       if ((source_desc != -1
426            ? fgetacl (source_desc, count, entries)
427            : getacl (src_name, count, entries))
428           == count)
429         break;
430       /* Huh? The number of ACL entries changed since the last call.
431          Repeat.  */
432     }
433
434   if (count == 0)
435     return qset_acl (dst_name, dest_desc, mode);
436
437   ret = (dest_desc != -1
438          ? fsetacl (dest_desc, count, entries)
439          : setacl (dst_name, count, entries));
440   if (ret < 0)
441     {
442       int saved_errno = errno;
443
444       if (errno == ENOSYS || errno == EOPNOTSUPP)
445         {
446           struct stat source_statbuf;
447
448           if ((source_desc != -1
449                ? fstat (source_desc, &source_statbuf)
450                : stat (src_name, &source_statbuf)) == 0)
451             {
452               if (!acl_nontrivial (count, entries, &source_statbuf))
453                 return chmod_or_fchmod (dst_name, dest_desc, mode);
454             }
455           else
456             saved_errno = errno;
457         }
458
459       chmod_or_fchmod (dst_name, dest_desc, mode);
460       errno = saved_errno;
461       return -1;
462     }
463
464   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
465     {
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.  */
468
469       return chmod_or_fchmod (dst_name, dest_desc, mode);
470     }
471   return 0;
472
473 #elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
474
475   /* TODO */
476
477 #elif USE_ACL && HAVE_STATACL /* older AIX */
478
479   union { struct acl a; char room[4096]; } u;
480   int ret;
481
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)))
485       < 0)
486     return -2;
487
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));
491   if (ret < 0)
492     {
493       int saved_errno = errno;
494
495       chmod_or_fchmod (dst_name, dest_desc, mode);
496       errno = saved_errno;
497       return -1;
498     }
499
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.  */
502
503   return 0;
504
505 #else
506
507   return qset_acl (dst_name, dest_desc, mode);
508
509 #endif
510 }
511
512
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
516    DST_NAME.
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.  */
521
522 int
523 copy_acl (const char *src_name, int source_desc, const char *dst_name,
524           int dest_desc, mode_t mode)
525 {
526   int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode);
527   switch (ret)
528     {
529     case -2:
530       error (0, errno, "%s", quote (src_name));
531       return -1;
532
533     case -1:
534       error (0, errno, _("preserving permissions for %s"), quote (dst_name));
535       return -1;
536
537     default:
538       return 0;
539     }
540 }