re-mark 1.29b-2 as not yet uploaded (merge madness!)
[debian/tar] / gnu / set-permissions.c
1 /* set-permissions.c - set permissions of a file
2
3    Copyright (C) 2002-2003, 2005-2015 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 #if USE_ACL
27 # if ! defined HAVE_ACL_FROM_MODE && defined HAVE_ACL_FROM_TEXT /* FreeBSD, IRIX, Tru64 */
28 #  if HAVE_ACL_GET_FILE && !HAVE_ACL_TYPE_EXTENDED
29
30 static acl_t
31 acl_from_mode (mode_t mode)
32 {
33 #  if HAVE_ACL_FREE_TEXT /* Tru64 */
34   char acl_text[] = "u::---,g::---,o::---,";
35 #  else /* FreeBSD, IRIX */
36   char acl_text[] = "u::---,g::---,o::---";
37 #  endif
38
39   if (mode & S_IRUSR) acl_text[ 3] = 'r';
40   if (mode & S_IWUSR) acl_text[ 4] = 'w';
41   if (mode & S_IXUSR) acl_text[ 5] = 'x';
42   if (mode & S_IRGRP) acl_text[10] = 'r';
43   if (mode & S_IWGRP) acl_text[11] = 'w';
44   if (mode & S_IXGRP) acl_text[12] = 'x';
45   if (mode & S_IROTH) acl_text[17] = 'r';
46   if (mode & S_IWOTH) acl_text[18] = 'w';
47   if (mode & S_IXOTH) acl_text[19] = 'x';
48
49   return acl_from_text (acl_text);
50 }
51 #  endif
52 # endif
53
54 # if HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
55 static int
56 set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
57 {
58 #  ifdef ACE_GETACL
59   /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
60      file systems (whereas the other ones are used in UFS file systems).  */
61
62   /* The flags in the ace_t structure changed in a binary incompatible way
63      when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
64      How to distinguish the two conventions at runtime?
65      We fetch the existing ACL.  In the old convention, usually three ACEs have
66      a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
67      In the new convention, these values are not used.  */
68   int convention;
69
70   {
71     /* Initially, try to read the entries into a stack-allocated buffer.
72        Use malloc if it does not fit.  */
73     enum
74       {
75         alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
76         alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
77       };
78     ace_t buf[alloc_init];
79     size_t alloc = alloc_init;
80     ace_t *entries = buf;
81     ace_t *malloced = NULL;
82     int count;
83
84     for (;;)
85       {
86         count = (desc != -1
87                  ? facl (desc, ACE_GETACL, alloc, entries)
88                  : acl (name, ACE_GETACL, alloc, entries));
89         if (count < 0 && errno == ENOSPC)
90           {
91             /* Increase the size of the buffer.  */
92             free (malloced);
93             if (alloc > alloc_max / 2)
94               {
95                 errno = ENOMEM;
96                 return -1;
97               }
98             alloc = 2 * alloc; /* <= alloc_max */
99             entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
100             if (entries == NULL)
101               {
102                 errno = ENOMEM;
103                 return -1;
104               }
105             continue;
106           }
107         break;
108       }
109
110     if (count <= 0)
111       convention = -1;
112     else
113       {
114         int i;
115
116         convention = 0;
117         for (i = 0; i < count; i++)
118           if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
119             {
120               convention = 1;
121               break;
122             }
123       }
124     free (malloced);
125   }
126
127   if (convention >= 0)
128     {
129       ace_t entries[6];
130       int count;
131       int ret;
132
133       if (convention)
134         {
135           /* Running on Solaris 10.  */
136           entries[0].a_type = OLD_ALLOW;
137           entries[0].a_flags = OLD_ACE_OWNER;
138           entries[0].a_who = 0; /* irrelevant */
139           entries[0].a_access_mask = (mode >> 6) & 7;
140           entries[1].a_type = OLD_ALLOW;
141           entries[1].a_flags = OLD_ACE_GROUP;
142           entries[1].a_who = 0; /* irrelevant */
143           entries[1].a_access_mask = (mode >> 3) & 7;
144           entries[2].a_type = OLD_ALLOW;
145           entries[2].a_flags = OLD_ACE_OTHER;
146           entries[2].a_who = 0;
147           entries[2].a_access_mask = mode & 7;
148           count = 3;
149         }
150       else
151         {
152           /* Running on Solaris 10 (newer version) or Solaris 11.
153              The details here were found through "/bin/ls -lvd somefiles".  */
154           entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
155           entries[0].a_flags = NEW_ACE_OWNER;
156           entries[0].a_who = 0; /* irrelevant */
157           entries[0].a_access_mask = 0;
158           entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
159           entries[1].a_flags = NEW_ACE_OWNER;
160           entries[1].a_who = 0; /* irrelevant */
161           entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
162                                      | NEW_ACE_WRITE_ATTRIBUTES
163                                      | NEW_ACE_WRITE_ACL
164                                      | NEW_ACE_WRITE_OWNER;
165           if (mode & 0400)
166             entries[1].a_access_mask |= NEW_ACE_READ_DATA;
167           else
168             entries[0].a_access_mask |= NEW_ACE_READ_DATA;
169           if (mode & 0200)
170             entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
171           else
172             entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
173           if (mode & 0100)
174             entries[1].a_access_mask |= NEW_ACE_EXECUTE;
175           else
176             entries[0].a_access_mask |= NEW_ACE_EXECUTE;
177           entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
178           entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
179           entries[2].a_who = 0; /* irrelevant */
180           entries[2].a_access_mask = 0;
181           entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
182           entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
183           entries[3].a_who = 0; /* irrelevant */
184           entries[3].a_access_mask = 0;
185           if (mode & 0040)
186             entries[3].a_access_mask |= NEW_ACE_READ_DATA;
187           else
188             entries[2].a_access_mask |= NEW_ACE_READ_DATA;
189           if (mode & 0020)
190             entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
191           else
192             entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
193           if (mode & 0010)
194             entries[3].a_access_mask |= NEW_ACE_EXECUTE;
195           else
196             entries[2].a_access_mask |= NEW_ACE_EXECUTE;
197           entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
198           entries[4].a_flags = NEW_ACE_EVERYONE;
199           entries[4].a_who = 0;
200           entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
201                                      | NEW_ACE_WRITE_ATTRIBUTES
202                                      | NEW_ACE_WRITE_ACL
203                                      | NEW_ACE_WRITE_OWNER;
204           entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
205           entries[5].a_flags = NEW_ACE_EVERYONE;
206           entries[5].a_who = 0;
207           entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
208                                      | NEW_ACE_READ_ATTRIBUTES
209                                      | NEW_ACE_READ_ACL
210                                      | NEW_ACE_SYNCHRONIZE;
211           if (mode & 0004)
212             entries[5].a_access_mask |= NEW_ACE_READ_DATA;
213           else
214             entries[4].a_access_mask |= NEW_ACE_READ_DATA;
215           if (mode & 0002)
216             entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
217           else
218             entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
219           if (mode & 0001)
220             entries[5].a_access_mask |= NEW_ACE_EXECUTE;
221           else
222             entries[4].a_access_mask |= NEW_ACE_EXECUTE;
223           count = 6;
224         }
225       if (desc != -1)
226         ret = facl (desc, ACE_SETACL, count, entries);
227       else
228         ret = acl (name, ACE_SETACL, count, entries);
229       if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
230         {
231           if (errno == ENOSYS)
232             {
233               *must_chmod = true;
234               return 0;
235             }
236           return -1;
237         }
238       if (ret == 0)
239         return 0;
240     }
241 #  endif
242
243   {
244     aclent_t entries[3];
245     int ret;
246
247     entries[0].a_type = USER_OBJ;
248     entries[0].a_id = 0; /* irrelevant */
249     entries[0].a_perm = (mode >> 6) & 7;
250     entries[1].a_type = GROUP_OBJ;
251     entries[1].a_id = 0; /* irrelevant */
252     entries[1].a_perm = (mode >> 3) & 7;
253     entries[2].a_type = OTHER_OBJ;
254     entries[2].a_id = 0;
255     entries[2].a_perm = mode & 7;
256
257     if (desc != -1)
258       ret = facl (desc, SETACL,
259                   sizeof (entries) / sizeof (aclent_t), entries);
260     else
261       ret = acl (name, SETACL,
262                  sizeof (entries) / sizeof (aclent_t), entries);
263     if (ret < 0)
264       {
265         if (errno == ENOSYS || errno == EOPNOTSUPP)
266           {
267             *must_chmod = true;
268             return 0;
269           }
270         return -1;
271       }
272   }
273 }
274
275 # elif HAVE_GETACL /* HP-UX */
276 static int
277 context_acl_from_mode (struct permission_context *ctx, const char *name, int desc)
278 {
279   struct stat statbuf;
280   int ret;
281
282   if (desc != -1)
283     ret = fstat (desc, &statbuf);
284   else
285     ret = stat (name, &statbuf);
286   if (ret < 0)
287     return -1;
288
289   ctx->entries[0].uid = statbuf.st_uid;
290   ctx->entries[0].gid = ACL_NSGROUP;
291   ctx->entries[0].mode = (ctx->mode >> 6) & 7;
292   ctx->entries[1].uid = ACL_NSUSER;
293   ctx->entries[1].gid = statbuf.st_gid;
294   ctx->entries[1].mode = (ctx->mode >> 3) & 7;
295   ctx->entries[2].uid = ACL_NSUSER;
296   ctx->entries[2].gid = ACL_NSGROUP;
297   ctx->entries[2].mode = ctx->mode & 7;
298   ctx->count = 3;
299   return 0;
300 }
301
302 #  if HAVE_ACLV_H /* HP-UX >= 11.11 */
303 static int
304 context_aclv_from_mode (struct permission_context *ctx)
305 {
306   int ret;
307
308   ctx->aclv_entries[0].a_type = USER_OBJ;
309   ctx->aclv_entries[0].a_id = 0; /* irrelevant */
310   ctx->aclv_entries[0].a_perm = (ctx->mode >> 6) & 7;
311   ctx->aclv_entries[1].a_type = GROUP_OBJ;
312   ctx->aclv_entries[1].a_id = 0; /* irrelevant */
313   ctx->aclv_entries[1].a_perm = (ctx->mode >> 3) & 7;
314   ctx->aclv_entries[2].a_type = CLASS_OBJ;
315   ctx->aclv_entries[2].a_id = 0;
316   ctx->aclv_entries[2].a_perm = (ctx->mode >> 3) & 7;
317   ctx->aclv_entries[3].a_type = OTHER_OBJ;
318   ctx->aclv_entries[3].a_id = 0;
319   ctx->aclv_entries[3].a_perm = ctx->mode & 7;
320   ctx->aclv_count = 4;
321
322   ret = aclsort (ctx->aclv_count, 1, ctx->aclv_entries);
323   if (ret > 0)
324     abort ();
325   return ret;
326 }
327 #  endif
328
329 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
330 static int
331 set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
332 {
333   acl_type_list_t types;
334   size_t types_size = sizeof (types);
335   acl_type_t type;
336
337   if (aclx_gettypes (name, &types, &types_size) < 0
338       || types.num_entries == 0)
339     {
340       *must_chmod = true;
341       return 0;
342     }
343
344   /* XXX Do we need to clear all types of ACLs for the given file, or is it
345      sufficient to clear the first one?  */
346   type = types.entries[0];
347   if (type.u64 == ACL_AIXC)
348     {
349       union { struct acl a; char room[128]; } u;
350       int ret;
351
352       u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
353       u.a.acl_mode = mode & ~(S_IXACL | 0777);
354       u.a.u_access = (mode >> 6) & 7;
355       u.a.g_access = (mode >> 3) & 7;
356       u.a.o_access = mode & 7;
357
358       if (desc != -1)
359         ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
360                          type, &u.a, u.a.acl_len, mode);
361       else
362         ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
363                         type, &u.a, u.a.acl_len, mode);
364       if (!(ret < 0 && errno == ENOSYS))
365         return ret;
366     }
367   else if (type.u64 == ACL_NFS4)
368     {
369       union { nfs4_acl_int_t a; char room[128]; } u;
370       nfs4_ace_int_t *ace;
371       int ret;
372
373       u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
374       u.a.aclEntryN = 0;
375       ace = &u.a.aclEntry[0];
376       {
377         ace->flags = ACE4_ID_SPECIAL;
378         ace->aceWho.special_whoid = ACE4_WHO_OWNER;
379         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
380         ace->aceFlags = 0;
381         ace->aceMask =
382           (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
383           | (mode & 0200
384              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
385                | ACE4_ADD_SUBDIRECTORY
386              : 0)
387           | (mode & 0100 ? ACE4_EXECUTE : 0);
388         ace->aceWhoString[0] = '\0';
389         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
390         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
391         u.a.aclEntryN++;
392       }
393       {
394         ace->flags = ACE4_ID_SPECIAL;
395         ace->aceWho.special_whoid = ACE4_WHO_GROUP;
396         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
397         ace->aceFlags = 0;
398         ace->aceMask =
399           (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
400           | (mode & 0020
401              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
402                | ACE4_ADD_SUBDIRECTORY
403              : 0)
404           | (mode & 0010 ? ACE4_EXECUTE : 0);
405         ace->aceWhoString[0] = '\0';
406         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
407         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
408         u.a.aclEntryN++;
409       }
410       {
411         ace->flags = ACE4_ID_SPECIAL;
412         ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
413         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
414         ace->aceFlags = 0;
415         ace->aceMask =
416           (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
417           | (mode & 0002
418              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
419                | ACE4_ADD_SUBDIRECTORY
420              : 0)
421           | (mode & 0001 ? ACE4_EXECUTE : 0);
422         ace->aceWhoString[0] = '\0';
423         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
424         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
425         u.a.aclEntryN++;
426       }
427       u.a.aclLength = (char *) ace - (char *) &u.a;
428
429       if (desc != -1)
430         ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
431                          type, &u.a, u.a.aclLength, mode);
432       else
433         ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
434                         type, &u.a, u.a.aclLength, mode);
435       if (!(ret < 0 && errno == ENOSYS))
436         return ret;
437     }
438
439   *must_chmod = true;
440   return 0;
441 }
442
443 # elif HAVE_STATACL /* older AIX */
444 static int
445 context_acl_from_mode (struct permission_context *ctx)
446 {
447   ctx->u.a.acl_len = (char *) &ctx->u.a.acl_ext[0] - (char *) &ctx->u.a; /* no entries */
448   ctx->u.a.acl_mode = ctx->mode & ~(S_IXACL | 0777);
449   ctx->u.a.u_access = (ctx->mode >> 6) & 7;
450   ctx->u.a.g_access = (ctx->mode >> 3) & 7;
451   ctx->u.a.o_access = ctx->mode & 7;
452   ctx->have_u = true;
453   return 0;
454 }
455
456 # elif HAVE_ACLSORT /* NonStop Kernel */
457 static int
458 context_acl_from_mode (struct permission_context *ctx)
459 {
460   int ret;
461
462   ctx->entries[0].a_type = USER_OBJ;
463   ctx->entries[0].a_id = 0; /* irrelevant */
464   ctx->entries[0].a_perm = (ctx->mode >> 6) & 7;
465   ctx->entries[1].a_type = GROUP_OBJ;
466   ctx->entries[1].a_id = 0; /* irrelevant */
467   ctx->entries[1].a_perm = (ctx->mode >> 3) & 7;
468   ctx->entries[2].a_type = CLASS_OBJ;
469   ctx->entries[2].a_id = 0;
470   ctx->entries[2].a_perm = (ctx->mode >> 3) & 7;
471   ctx->entries[3].a_type = OTHER_OBJ;
472   ctx->entries[3].a_id = 0;
473   ctx->entries[3].a_perm = ctx->mode & 7;
474   ctx->count = 4;
475
476   ret = aclsort (ctx->count, 1, entries);
477   if (ret > 0)
478     abort ();
479   return ret;
480 }
481 # endif
482
483 static int
484 set_acls (struct permission_context *ctx, const char *name, int desc,
485           int from_mode, bool *must_chmod, bool *acls_set)
486 {
487   int ret = 0;
488
489 # if HAVE_ACL_GET_FILE
490   /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
491   /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
492 #  if !HAVE_ACL_TYPE_EXTENDED
493   /* Linux, FreeBSD, IRIX, Tru64 */
494
495 #   ifndef HAVE_ACL_FROM_TEXT
496 #    error Must have acl_from_text (see POSIX 1003.1e draft 17).
497 #   endif
498 #   ifndef HAVE_ACL_DELETE_DEF_FILE
499 #    error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
500 #   endif
501
502   if (! ctx->acls_not_supported)
503     {
504       if (ret == 0 && from_mode)
505         {
506           if (ctx->acl)
507             acl_free (ctx->acl);
508           ctx->acl = acl_from_mode (ctx->mode);
509           if (ctx->acl == NULL)
510             ret = -1;
511         }
512
513       if (ret == 0 && ctx->acl)
514         {
515           if (HAVE_ACL_SET_FD && desc != -1)
516             ret = acl_set_fd (desc, ctx->acl);
517           else
518             ret = acl_set_file (name, ACL_TYPE_ACCESS, ctx->acl);
519           if (ret != 0)
520             {
521               if (! acl_errno_valid (errno))
522                 {
523                   ctx->acls_not_supported = true;
524                   if (from_mode || acl_access_nontrivial (ctx->acl) == 0)
525                     ret = 0;
526                 }
527             }
528           else
529             {
530               *acls_set = true;
531               if (S_ISDIR(ctx->mode))
532                 {
533                   if (! from_mode && ctx->default_acl &&
534                       acl_default_nontrivial (ctx->default_acl))
535                     ret = acl_set_file (name, ACL_TYPE_DEFAULT,
536                                         ctx->default_acl);
537                   else
538                     ret = acl_delete_def_file (name);
539                 }
540             }
541         }
542     }
543
544 # if HAVE_ACL_TYPE_NFS4  /* FreeBSD */
545
546   /* File systems either support POSIX ACLs (for example, ufs) or NFS4 ACLs
547      (for example, zfs). */
548
549   /* TODO: Implement setting ACLs once get_permissions() reads them. */
550
551 # endif
552
553 #  else /* HAVE_ACL_TYPE_EXTENDED */
554   /* Mac OS X */
555
556   /* On Mac OS X,  acl_get_file (name, ACL_TYPE_ACCESS)
557      and           acl_get_file (name, ACL_TYPE_DEFAULT)
558      always return NULL / EINVAL.  You have to use
559                    acl_get_file (name, ACL_TYPE_EXTENDED)
560      or            acl_get_fd (open (name, ...))
561      to retrieve an ACL.
562      On the other hand,
563                    acl_set_file (name, ACL_TYPE_ACCESS, acl)
564      and           acl_set_file (name, ACL_TYPE_DEFAULT, acl)
565      have the same effect as
566                    acl_set_file (name, ACL_TYPE_EXTENDED, acl):
567      Each of these calls sets the file's ACL.  */
568
569   if (ctx->acl == NULL)
570     {
571       acl_t acl;
572
573       /* Remove ACLs if the file has ACLs.  */
574       if (HAVE_ACL_GET_FD && desc != -1)
575         acl = acl_get_fd (desc);
576       else
577         acl = acl_get_file (name, ACL_TYPE_EXTENDED);
578       if (acl)
579         {
580           acl_free (acl);
581
582           acl = acl_init (0);
583           if (acl)
584             {
585               if (HAVE_ACL_SET_FD && desc != -1)
586                 ret = acl_set_fd (desc, acl);
587               else
588                 ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
589               acl_free (acl);
590             }
591           else
592             ret = -1;
593         }
594     }
595   else
596     {
597       if (HAVE_ACL_SET_FD && desc != -1)
598         ret = acl_set_fd (desc, ctx->acl);
599       else
600         ret = acl_set_file (name, ACL_TYPE_EXTENDED, ctx->acl);
601       if (ret != 0)
602         {
603           if (! acl_errno_valid (errno)
604               && ! acl_extended_nontrivial (ctx->acl))
605             ret = 0;
606         }
607     }
608   *acls_set = true;
609
610 #  endif
611
612 # elif defined GETACL /* Solaris, Cygwin, not HP-UX */
613
614   /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
615      of Unixware.  The acl() call returns the access and default ACL both
616      at once.  */
617
618   /* If both ace_entries and entries are available, try SETACL before
619      ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
620      can.  */
621
622   if (from_mode)
623     return set_acls_from_mode (name, desc, ctx->mode, must_chmod);
624
625   if (ret == 0 && ctx->count)
626     {
627       if (desc != -1)
628         ret = facl (desc, SETACL, ctx->count, ctx->entries);
629       else
630         ret = acl (name, SETACL, ctx->count, ctx->entries);
631       if (ret < 0)
632         {
633           if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
634               && acl_nontrivial (ctx->count, ctx->entries) == 0)
635             ret = 0;
636         }
637       else
638         *acls_set = true;
639     }
640
641 #  ifdef ACE_GETACL
642   if (ret == 0 && ctx->ace_count)
643     {
644       if (desc != -1)
645         ret = facl (desc, ACE_SETACL, ctx->ace_count, ctx->ace_entries);
646       else
647         ret = acl (name, ACE_SETACL, ctx->ace_count, ctx->ace_entries);
648       if (ret < 0)
649         {
650           if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
651               && acl_ace_nontrivial (ctx->ace_count, ctx->ace_entries) == 0)
652             ret = 0;
653         }
654       else
655         *acls_set = true;
656     }
657 #  endif
658
659 # elif HAVE_GETACL /* HP-UX */
660
661   if (from_mode)
662     ret = context_acl_from_mode (ctx, name, desc);
663
664   if (ret == 0 && ctx->count > 0)
665     {
666       if (desc != -1)
667         ret = fsetacl (desc, ctx->count, ctx->entries);
668       else
669         ret = setacl (name, ctx->count, ctx->entries);
670       if (ret < 0)
671         {
672           if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
673               && (from_mode || !acl_nontrivial (ctx->count, ctx->entries)))
674             ret = 0;
675         }
676       else
677         *acls_set = true;
678     }
679
680 #  if HAVE_ACLV_H
681   if (from_mode)
682     ret = context_aclv_from_mode (ctx);
683
684   if (ret == 0 && ctx->aclv_count > 0)
685     {
686       ret = acl ((char *) name, ACL_SET, ctx->aclv_count, ctx->aclv_entries);
687       if (ret < 0)
688         {
689           if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
690               && (from_mode || !aclv_nontrivial (ctx->aclv_count, ctx->aclv_entries)))
691             ret = 0;
692         }
693       else
694         *acls_set = true;
695     }
696 #  endif
697
698 # elif HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */
699
700   /* TODO: Implement setting ACLs once get_permissions() reads them. */
701
702   if (from_mode)
703     ret = set_acls_from_mode (name, desc, mode, must_chmod);
704
705 # elif HAVE_STATACL /* older AIX */
706
707   if (from_mode)
708     ret = context_acl_from_mode (ctx);
709
710   if (ret == 0 && ctx->have_u)
711     {
712       if (desc != -1)
713         ret = fchacl (desc, &ctx->u.a, ctx->u.a.acl_len);
714       else
715         ret = chacl ((char *) name, &ctx->u.a, ctx->u.a.acl_len);
716       if (ret < 0)
717         {
718           if (errno == ENOSYS && from_mode)
719             ret = 0;
720         }
721       else
722         *acls_set = true;
723     }
724
725 # elif HAVE_ACLSORT /* NonStop Kernel */
726
727   if (from_mode)
728     ret = context_acl_from_mode (ctx);
729
730   if (ret == 0 && ctx->count)
731     {
732       ret = acl ((char *) name, ACL_SET, ctx->count, ctx->entries);
733       if (ret != 0)
734         {
735           if (!acl_nontrivial (ctx->count, ctx->entries))
736             ret = 0;
737         }
738       else
739         *acls_set = true;
740     }
741
742 # else  /* No ACLs */
743
744   /* Nothing to do. */
745
746 # endif
747
748   return ret;
749 }
750 #endif
751
752 /* If DESC is a valid file descriptor use fchmod to change the
753    file's mode to MODE on systems that have fchmod. On systems
754    that don't have fchmod and if DESC is invalid, use chmod on
755    NAME instead.
756    Return 0 if successful.  Return -1 and set errno upon failure.  */
757
758 int
759 chmod_or_fchmod (const char *name, int desc, mode_t mode)
760 {
761   if (HAVE_FCHMOD && desc != -1)
762     return fchmod (desc, mode);
763   else
764     return chmod (name, mode);
765 }
766
767 /* Set the permissions in CTX on a file. If DESC is a valid file descriptor,
768    use file descriptor operations, else use filename based operations on NAME.
769    If access control lists are not available, fchmod the target file to the
770    mode in CTX.  Also sets the non-permission bits of the destination file
771    (S_ISUID, S_ISGID, S_ISVTX) to those from the mode in CTX if any are set.
772    Return 0 if successful.  Return -1 and set errno upon failure.  */
773
774 int
775 set_permissions (struct permission_context *ctx, const char *name, int desc)
776 {
777   bool acls_set _GL_UNUSED = false;
778   bool early_chmod;
779   bool must_chmod = false;
780   int ret = 0;
781
782 #if USE_ACL
783 # if HAVE_STATACL
784   /* older AIX */
785   /* There is no need to call chmod_or_fchmod, since the mode
786      bits S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL.  */
787
788   early_chmod = false;
789 # else
790   /* All other platforms */
791   /* On Cygwin, it is necessary to call chmod before acl, because
792      chmod can change the contents of the ACL (in ways that don't
793      change the allowed accesses, but still visible).  */
794
795   early_chmod = (! MODE_INSIDE_ACL || (ctx->mode & (S_ISUID | S_ISGID | S_ISVTX)));
796 # endif
797 #else
798   /* No ACLs */
799
800   early_chmod = true;
801 #endif
802
803   if (early_chmod)
804     {
805       ret = chmod_or_fchmod (name, desc, ctx->mode);
806       if (ret != 0)
807         return -1;
808     }
809
810 #if USE_ACL
811   ret = set_acls (ctx, name, desc, false, &must_chmod, &acls_set);
812   if (! acls_set)
813     {
814       int saved_errno = ret ? errno : 0;
815
816       /* If we can't set an acl which we expect to be able to set, try setting
817          the permissions to ctx->mode. Due to possible inherited permissions,
818          we cannot simply chmod.  */
819
820       ret = set_acls (ctx, name, desc, true, &must_chmod, &acls_set);
821       if (! acls_set)
822         must_chmod = true;
823
824       if (saved_errno)
825         {
826           errno = saved_errno;
827           ret = -1;
828         }
829     }
830 #endif
831
832   if (must_chmod && ! early_chmod)
833     {
834       int saved_errno = ret ? errno : 0;
835
836       ret = chmod_or_fchmod (name, desc, ctx->mode);
837
838       if (saved_errno)
839         {
840           errno = saved_errno;
841           ret = -1;
842         }
843     }
844
845   return ret;
846 }