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