Fix test failure on Cygwin.
[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 (!MODE_INSIDE_ACL)
340     {
341       /* On Cygwin, it is necessary to call chmod before acl, because
342          chmod can change the contents of the ACL (in ways that don't
343          change the allowed accesses, but still visible).  */
344       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
345         saved_errno = errno;
346       did_chmod = 1;
347     }
348
349   /* If both ace_entries and entries are available, try SETACL before
350      ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
351      can.  */
352
353   if (count > 0)
354     {
355       ret = (dest_desc != -1
356              ? facl (dest_desc, SETACL, count, entries)
357              : acl (dst_name, SETACL, count, entries));
358       if (ret < 0 && saved_errno == 0)
359         {
360           saved_errno = errno;
361           if (errno == ENOSYS && !acl_nontrivial (count, entries))
362             saved_errno = 0;
363         }
364       else
365         did_chmod = 1;
366     }
367   free (entries);
368
369 #  ifdef ACE_GETACL
370   if (ace_count > 0)
371     {
372       ret = (dest_desc != -1
373              ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries)
374              : acl (dst_name, ACE_SETACL, ace_count, ace_entries));
375       if (ret < 0 && saved_errno == 0)
376         {
377           saved_errno = errno;
378           if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
379               && !acl_ace_nontrivial (ace_count, ace_entries))
380             saved_errno = 0;
381         }
382     }
383   free (ace_entries);
384 #  endif
385
386   if (MODE_INSIDE_ACL
387       && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
388     {
389       /* We did not call chmod so far, and either the mode and the ACL are
390          separate or special bits are to be set which don't fit into ACLs.  */
391
392       if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
393         {
394           if (saved_errno == 0)
395             saved_errno = errno;
396         }
397     }
398
399   if (saved_errno)
400     {
401       errno = saved_errno;
402       return -1;
403     }
404   return 0;
405
406 # endif
407
408 #elif USE_ACL && HAVE_GETACL /* HP-UX */
409
410   int count;
411   struct acl_entry entries[NACLENTRIES];
412   int ret;
413
414   for (;;)
415     {
416       count = (source_desc != -1
417                ? fgetacl (source_desc, 0, NULL)
418                : getacl (src_name, 0, NULL));
419
420       if (count < 0)
421         {
422           if (errno == ENOSYS || errno == EOPNOTSUPP)
423             {
424               count = 0;
425               break;
426             }
427           else
428             return -2;
429         }
430
431       if (count == 0)
432         break;
433
434       if (count > NACLENTRIES)
435         /* If NACLENTRIES cannot be trusted, use dynamic memory allocation.  */
436         abort ();
437
438       if ((source_desc != -1
439            ? fgetacl (source_desc, count, entries)
440            : getacl (src_name, count, entries))
441           == count)
442         break;
443       /* Huh? The number of ACL entries changed since the last call.
444          Repeat.  */
445     }
446
447   if (count == 0)
448     return qset_acl (dst_name, dest_desc, mode);
449
450   ret = (dest_desc != -1
451          ? fsetacl (dest_desc, count, entries)
452          : setacl (dst_name, count, entries));
453   if (ret < 0)
454     {
455       int saved_errno = errno;
456
457       if (errno == ENOSYS || errno == EOPNOTSUPP)
458         {
459           struct stat source_statbuf;
460
461           if ((source_desc != -1
462                ? fstat (source_desc, &source_statbuf)
463                : stat (src_name, &source_statbuf)) == 0)
464             {
465               if (!acl_nontrivial (count, entries, &source_statbuf))
466                 return chmod_or_fchmod (dst_name, dest_desc, mode);
467             }
468           else
469             saved_errno = errno;
470         }
471
472       chmod_or_fchmod (dst_name, dest_desc, mode);
473       errno = saved_errno;
474       return -1;
475     }
476
477   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
478     {
479       /* We did not call chmod so far, and either the mode and the ACL are
480          separate or special bits are to be set which don't fit into ACLs.  */
481
482       return chmod_or_fchmod (dst_name, dest_desc, mode);
483     }
484   return 0;
485
486 #elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
487
488   /* TODO */
489
490 #elif USE_ACL && HAVE_STATACL /* older AIX */
491
492   union { struct acl a; char room[4096]; } u;
493   int ret;
494
495   if ((source_desc != -1
496        ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u))
497        : statacl (src_name, STX_NORMAL, &u.a, sizeof (u)))
498       < 0)
499     return -2;
500
501   ret = (dest_desc != -1
502          ? fchacl (dest_desc, &u.a, u.a.acl_len)
503          : chacl (dst_name, &u.a, u.a.acl_len));
504   if (ret < 0)
505     {
506       int saved_errno = errno;
507
508       chmod_or_fchmod (dst_name, dest_desc, mode);
509       errno = saved_errno;
510       return -1;
511     }
512
513   /* No need to call chmod_or_fchmod at this point, since the mode bits
514      S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL.  */
515
516   return 0;
517
518 #else
519
520   return qset_acl (dst_name, dest_desc, mode);
521
522 #endif
523 }
524
525
526 /* Copy access control lists from one file to another. If SOURCE_DESC is
527    a valid file descriptor, use file descriptor operations, else use
528    filename based operations on SRC_NAME. Likewise for DEST_DESC and
529    DST_NAME.
530    If access control lists are not available, fchmod the target file to
531    MODE.  Also sets the non-permission bits of the destination file
532    (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
533    Return 0 if successful, otherwise output a diagnostic and return -1.  */
534
535 int
536 copy_acl (const char *src_name, int source_desc, const char *dst_name,
537           int dest_desc, mode_t mode)
538 {
539   int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode);
540   switch (ret)
541     {
542     case -2:
543       error (0, errno, "%s", quote (src_name));
544       return -1;
545
546     case -1:
547       error (0, errno, _("preserving permissions for %s"), quote (dst_name));
548       return -1;
549
550     default:
551       return 0;
552     }
553 }