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