maint: update all copyright year number ranges
[gnulib.git] / lib / set-mode-acl.c
1 /* set-mode-acl.c - set access control list equivalent to a mode
2
3    Copyright (C) 2002-2003, 2005-2013 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 and Andreas Gruenbacher, and Bruno Haible.  */
19
20 #include <config.h>
21
22 #define ACL_INTERNAL_INLINE _GL_EXTERN_INLINE
23
24 #include "acl.h"
25
26 #include "acl-internal.h"
27
28 #include "gettext.h"
29 #define _(msgid) gettext (msgid)
30
31
32 /* If DESC is a valid file descriptor use fchmod to change the
33    file's mode to MODE on systems that have fchown. On systems
34    that don't have fchown and if DESC is invalid, use chown on
35    NAME instead.
36    Return 0 if successful.  Return -1 and set errno upon failure.  */
37
38 int
39 chmod_or_fchmod (const char *name, int desc, mode_t mode)
40 {
41   if (HAVE_FCHMOD && desc != -1)
42     return fchmod (desc, mode);
43   else
44     return chmod (name, mode);
45 }
46
47 /* Set the access control lists of a file. If DESC is a valid file
48    descriptor, use file descriptor operations where available, else use
49    filename based operations on NAME.  If access control lists are not
50    available, fchmod the target file to MODE.  Also sets the
51    non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX)
52    to those from MODE if any are set.
53    Return 0 if successful.  Return -1 and set errno upon failure.  */
54
55 int
56 qset_acl (char const *name, int desc, mode_t mode)
57 {
58 #if USE_ACL
59 # if HAVE_ACL_GET_FILE
60   /* POSIX 1003.1e draft 17 (abandoned) specific version.  */
61   /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
62 #  if !HAVE_ACL_TYPE_EXTENDED
63   /* Linux, FreeBSD, IRIX, Tru64 */
64
65   /* We must also have acl_from_text and acl_delete_def_file.
66      (acl_delete_def_file could be emulated with acl_init followed
67       by acl_set_file, but acl_set_file with an empty acl is
68       unspecified.)  */
69
70 #   ifndef HAVE_ACL_FROM_TEXT
71 #    error Must have acl_from_text (see POSIX 1003.1e draft 17).
72 #   endif
73 #   ifndef HAVE_ACL_DELETE_DEF_FILE
74 #    error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
75 #   endif
76
77   acl_t acl;
78   int ret;
79
80   if (HAVE_ACL_FROM_MODE) /* Linux */
81     {
82       acl = acl_from_mode (mode);
83       if (!acl)
84         return -1;
85     }
86   else /* FreeBSD, IRIX, Tru64 */
87     {
88       /* If we were to create the ACL using the functions acl_init(),
89          acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(),
90          acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we
91          would need to create a qualifier.  I don't know how to do this.
92          So create it using acl_from_text().  */
93
94 #   if HAVE_ACL_FREE_TEXT /* Tru64 */
95       char acl_text[] = "u::---,g::---,o::---,";
96 #   else /* FreeBSD, IRIX */
97       char acl_text[] = "u::---,g::---,o::---";
98 #   endif
99
100       if (mode & S_IRUSR) acl_text[ 3] = 'r';
101       if (mode & S_IWUSR) acl_text[ 4] = 'w';
102       if (mode & S_IXUSR) acl_text[ 5] = 'x';
103       if (mode & S_IRGRP) acl_text[10] = 'r';
104       if (mode & S_IWGRP) acl_text[11] = 'w';
105       if (mode & S_IXGRP) acl_text[12] = 'x';
106       if (mode & S_IROTH) acl_text[17] = 'r';
107       if (mode & S_IWOTH) acl_text[18] = 'w';
108       if (mode & S_IXOTH) acl_text[19] = 'x';
109
110       acl = acl_from_text (acl_text);
111       if (!acl)
112         return -1;
113     }
114   if (HAVE_ACL_SET_FD && desc != -1)
115     ret = acl_set_fd (desc, acl);
116   else
117     ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
118   if (ret != 0)
119     {
120       int saved_errno = errno;
121       acl_free (acl);
122
123       if (ACL_NOT_WELL_SUPPORTED (errno))
124         return chmod_or_fchmod (name, desc, mode);
125       else
126         {
127           errno = saved_errno;
128           return -1;
129         }
130     }
131   else
132     acl_free (acl);
133
134   if (S_ISDIR (mode) && acl_delete_def_file (name))
135     return -1;
136
137   if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
138     {
139       /* We did not call chmod so far, and either the mode and the ACL are
140          separate or special bits are to be set which don't fit into ACLs.  */
141       return chmod_or_fchmod (name, desc, mode);
142     }
143   return 0;
144
145 #  else /* HAVE_ACL_TYPE_EXTENDED */
146   /* Mac OS X */
147
148   /* On Mac OS X,  acl_get_file (name, ACL_TYPE_ACCESS)
149      and           acl_get_file (name, ACL_TYPE_DEFAULT)
150      always return NULL / EINVAL.  You have to use
151                    acl_get_file (name, ACL_TYPE_EXTENDED)
152      or            acl_get_fd (open (name, ...))
153      to retrieve an ACL.
154      On the other hand,
155                    acl_set_file (name, ACL_TYPE_ACCESS, acl)
156      and           acl_set_file (name, ACL_TYPE_DEFAULT, acl)
157      have the same effect as
158                    acl_set_file (name, ACL_TYPE_EXTENDED, acl):
159      Each of these calls sets the file's ACL.  */
160
161   acl_t acl;
162   int ret;
163
164   /* Remove the ACL if the file has ACLs.  */
165   if (HAVE_ACL_GET_FD && desc != -1)
166     acl = acl_get_fd (desc);
167   else
168     acl = acl_get_file (name, ACL_TYPE_EXTENDED);
169   if (acl)
170     {
171       acl_free (acl);
172
173       acl = acl_init (0);
174       if (acl)
175         {
176           if (HAVE_ACL_SET_FD && desc != -1)
177             ret = acl_set_fd (desc, acl);
178           else
179             ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
180           if (ret != 0)
181             {
182               int saved_errno = errno;
183
184               acl_free (acl);
185
186               if (ACL_NOT_WELL_SUPPORTED (saved_errno))
187                 return chmod_or_fchmod (name, desc, mode);
188               else
189                 {
190                   errno = saved_errno;
191                   return -1;
192                 }
193             }
194           acl_free (acl);
195         }
196     }
197
198   /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly.  */
199   return chmod_or_fchmod (name, desc, mode);
200 #  endif
201
202 # elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
203
204   int done_setacl = 0;
205
206 #  ifdef ACE_GETACL
207   /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
208      file systems (whereas the other ones are used in UFS file systems).  */
209
210   /* The flags in the ace_t structure changed in a binary incompatible way
211      when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
212      How to distinguish the two conventions at runtime?
213      We fetch the existing ACL.  In the old convention, usually three ACEs have
214      a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
215      In the new convention, these values are not used.  */
216   int convention;
217
218   {
219     /* Initially, try to read the entries into a stack-allocated buffer.
220        Use malloc if it does not fit.  */
221     enum
222       {
223         alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
224         alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
225       };
226     ace_t buf[alloc_init];
227     size_t alloc = alloc_init;
228     ace_t *entries = buf;
229     ace_t *malloced = NULL;
230     int count;
231
232     for (;;)
233       {
234         count = (desc != -1
235                  ? facl (desc, ACE_GETACL, alloc, entries)
236                  : acl (name, ACE_GETACL, alloc, entries));
237         if (count < 0 && errno == ENOSPC)
238           {
239             /* Increase the size of the buffer.  */
240             free (malloced);
241             if (alloc > alloc_max / 2)
242               {
243                 errno = ENOMEM;
244                 return -1;
245               }
246             alloc = 2 * alloc; /* <= alloc_max */
247             entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
248             if (entries == NULL)
249               {
250                 errno = ENOMEM;
251                 return -1;
252               }
253             continue;
254           }
255         break;
256       }
257
258     if (count <= 0)
259       convention = -1;
260     else
261       {
262         int i;
263
264         convention = 0;
265         for (i = 0; i < count; i++)
266           if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
267             {
268               convention = 1;
269               break;
270             }
271       }
272     free (malloced);
273   }
274
275   if (convention >= 0)
276     {
277       ace_t entries[6];
278       int count;
279       int ret;
280
281       if (convention)
282         {
283           /* Running on Solaris 10.  */
284           entries[0].a_type = OLD_ALLOW;
285           entries[0].a_flags = OLD_ACE_OWNER;
286           entries[0].a_who = 0; /* irrelevant */
287           entries[0].a_access_mask = (mode >> 6) & 7;
288           entries[1].a_type = OLD_ALLOW;
289           entries[1].a_flags = OLD_ACE_GROUP;
290           entries[1].a_who = 0; /* irrelevant */
291           entries[1].a_access_mask = (mode >> 3) & 7;
292           entries[2].a_type = OLD_ALLOW;
293           entries[2].a_flags = OLD_ACE_OTHER;
294           entries[2].a_who = 0;
295           entries[2].a_access_mask = mode & 7;
296           count = 3;
297         }
298       else
299         {
300           /* Running on Solaris 10 (newer version) or Solaris 11.
301              The details here were found through "/bin/ls -lvd somefiles".  */
302           entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
303           entries[0].a_flags = NEW_ACE_OWNER;
304           entries[0].a_who = 0; /* irrelevant */
305           entries[0].a_access_mask = 0;
306           entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
307           entries[1].a_flags = NEW_ACE_OWNER;
308           entries[1].a_who = 0; /* irrelevant */
309           entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
310                                      | NEW_ACE_WRITE_ATTRIBUTES
311                                      | NEW_ACE_WRITE_ACL
312                                      | NEW_ACE_WRITE_OWNER;
313           if (mode & 0400)
314             entries[1].a_access_mask |= NEW_ACE_READ_DATA;
315           else
316             entries[0].a_access_mask |= NEW_ACE_READ_DATA;
317           if (mode & 0200)
318             entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
319           else
320             entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
321           if (mode & 0100)
322             entries[1].a_access_mask |= NEW_ACE_EXECUTE;
323           else
324             entries[0].a_access_mask |= NEW_ACE_EXECUTE;
325           entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
326           entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
327           entries[2].a_who = 0; /* irrelevant */
328           entries[2].a_access_mask = 0;
329           entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
330           entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
331           entries[3].a_who = 0; /* irrelevant */
332           entries[3].a_access_mask = 0;
333           if (mode & 0040)
334             entries[3].a_access_mask |= NEW_ACE_READ_DATA;
335           else
336             entries[2].a_access_mask |= NEW_ACE_READ_DATA;
337           if (mode & 0020)
338             entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
339           else
340             entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
341           if (mode & 0010)
342             entries[3].a_access_mask |= NEW_ACE_EXECUTE;
343           else
344             entries[2].a_access_mask |= NEW_ACE_EXECUTE;
345           entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
346           entries[4].a_flags = NEW_ACE_EVERYONE;
347           entries[4].a_who = 0;
348           entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
349                                      | NEW_ACE_WRITE_ATTRIBUTES
350                                      | NEW_ACE_WRITE_ACL
351                                      | NEW_ACE_WRITE_OWNER;
352           entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
353           entries[5].a_flags = NEW_ACE_EVERYONE;
354           entries[5].a_who = 0;
355           entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
356                                      | NEW_ACE_READ_ATTRIBUTES
357                                      | NEW_ACE_READ_ACL
358                                      | NEW_ACE_SYNCHRONIZE;
359           if (mode & 0004)
360             entries[5].a_access_mask |= NEW_ACE_READ_DATA;
361           else
362             entries[4].a_access_mask |= NEW_ACE_READ_DATA;
363           if (mode & 0002)
364             entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
365           else
366             entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
367           if (mode & 0001)
368             entries[5].a_access_mask |= NEW_ACE_EXECUTE;
369           else
370             entries[4].a_access_mask |= NEW_ACE_EXECUTE;
371           count = 6;
372         }
373       if (desc != -1)
374         ret = facl (desc, ACE_SETACL, count, entries);
375       else
376         ret = acl (name, ACE_SETACL, count, entries);
377       if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
378         {
379           if (errno == ENOSYS)
380             return chmod_or_fchmod (name, desc, mode);
381           return -1;
382         }
383       if (ret == 0)
384         done_setacl = 1;
385     }
386 #  endif
387
388   if (!done_setacl)
389     {
390       aclent_t entries[3];
391       int ret;
392
393       entries[0].a_type = USER_OBJ;
394       entries[0].a_id = 0; /* irrelevant */
395       entries[0].a_perm = (mode >> 6) & 7;
396       entries[1].a_type = GROUP_OBJ;
397       entries[1].a_id = 0; /* irrelevant */
398       entries[1].a_perm = (mode >> 3) & 7;
399       entries[2].a_type = OTHER_OBJ;
400       entries[2].a_id = 0;
401       entries[2].a_perm = mode & 7;
402
403       if (desc != -1)
404         ret = facl (desc, SETACL,
405                     sizeof (entries) / sizeof (aclent_t), entries);
406       else
407         ret = acl (name, SETACL,
408                    sizeof (entries) / sizeof (aclent_t), entries);
409       if (ret < 0)
410         {
411           if (errno == ENOSYS || errno == EOPNOTSUPP)
412             return chmod_or_fchmod (name, desc, mode);
413           return -1;
414         }
415     }
416
417   if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
418     {
419       /* We did not call chmod so far, so the special bits have not yet
420          been set.  */
421       return chmod_or_fchmod (name, desc, mode);
422     }
423   return 0;
424
425 # elif HAVE_GETACL /* HP-UX */
426
427   struct stat statbuf;
428   int ret;
429
430   if (desc != -1)
431     ret = fstat (desc, &statbuf);
432   else
433     ret = stat (name, &statbuf);
434   if (ret < 0)
435     return -1;
436
437   {
438     struct acl_entry entries[3];
439
440     entries[0].uid = statbuf.st_uid;
441     entries[0].gid = ACL_NSGROUP;
442     entries[0].mode = (mode >> 6) & 7;
443     entries[1].uid = ACL_NSUSER;
444     entries[1].gid = statbuf.st_gid;
445     entries[1].mode = (mode >> 3) & 7;
446     entries[2].uid = ACL_NSUSER;
447     entries[2].gid = ACL_NSGROUP;
448     entries[2].mode = mode & 7;
449
450     if (desc != -1)
451       ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
452     else
453       ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
454   }
455   if (ret < 0)
456     {
457       if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
458         return -1;
459
460 #  if HAVE_ACLV_H /* HP-UX >= 11.11 */
461       {
462         struct acl entries[4];
463
464         entries[0].a_type = USER_OBJ;
465         entries[0].a_id = 0; /* irrelevant */
466         entries[0].a_perm = (mode >> 6) & 7;
467         entries[1].a_type = GROUP_OBJ;
468         entries[1].a_id = 0; /* irrelevant */
469         entries[1].a_perm = (mode >> 3) & 7;
470         entries[2].a_type = CLASS_OBJ;
471         entries[2].a_id = 0;
472         entries[2].a_perm = (mode >> 3) & 7;
473         entries[3].a_type = OTHER_OBJ;
474         entries[3].a_id = 0;
475         entries[3].a_perm = mode & 7;
476
477         ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
478         if (ret > 0)
479           abort ();
480         if (ret < 0)
481           {
482             if (0)
483               return chmod_or_fchmod (name, desc, mode);
484             return -1;
485           }
486
487         ret = acl ((char *) name, ACL_SET,
488                    sizeof (entries) / sizeof (struct acl), entries);
489         if (ret < 0)
490           {
491             if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
492               return chmod_or_fchmod (name, desc, mode);
493             return -1;
494           }
495       }
496 #  else
497       return chmod_or_fchmod (name, desc, mode);
498 #  endif
499     }
500
501   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
502     {
503       /* We did not call chmod so far, so the special bits have not yet
504          been set.  */
505       return chmod_or_fchmod (name, desc, mode);
506     }
507   return 0;
508
509 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
510
511   acl_type_list_t types;
512   size_t types_size = sizeof (types);
513   acl_type_t type;
514
515   if (aclx_gettypes (name, &types, &types_size) < 0
516       || types.num_entries == 0)
517     return chmod_or_fchmod (name, desc, mode);
518
519   /* XXX Do we need to clear all types of ACLs for the given file, or is it
520      sufficient to clear the first one?  */
521   type = types.entries[0];
522   if (type.u64 == ACL_AIXC)
523     {
524       union { struct acl a; char room[128]; } u;
525       int ret;
526
527       u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
528       u.a.acl_mode = mode & ~(S_IXACL | 0777);
529       u.a.u_access = (mode >> 6) & 7;
530       u.a.g_access = (mode >> 3) & 7;
531       u.a.o_access = mode & 7;
532
533       if (desc != -1)
534         ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
535                          type, &u.a, u.a.acl_len, mode);
536       else
537         ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
538                         type, &u.a, u.a.acl_len, mode);
539       if (!(ret < 0 && errno == ENOSYS))
540         return ret;
541     }
542   else if (type.u64 == ACL_NFS4)
543     {
544       union { nfs4_acl_int_t a; char room[128]; } u;
545       nfs4_ace_int_t *ace;
546       int ret;
547
548       u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
549       u.a.aclEntryN = 0;
550       ace = &u.a.aclEntry[0];
551       {
552         ace->flags = ACE4_ID_SPECIAL;
553         ace->aceWho.special_whoid = ACE4_WHO_OWNER;
554         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
555         ace->aceFlags = 0;
556         ace->aceMask =
557           (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
558           | (mode & 0200
559              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
560                | ACE4_ADD_SUBDIRECTORY
561              : 0)
562           | (mode & 0100 ? ACE4_EXECUTE : 0);
563         ace->aceWhoString[0] = '\0';
564         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
565         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
566         u.a.aclEntryN++;
567       }
568       {
569         ace->flags = ACE4_ID_SPECIAL;
570         ace->aceWho.special_whoid = ACE4_WHO_GROUP;
571         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
572         ace->aceFlags = 0;
573         ace->aceMask =
574           (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
575           | (mode & 0020
576              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
577                | ACE4_ADD_SUBDIRECTORY
578              : 0)
579           | (mode & 0010 ? ACE4_EXECUTE : 0);
580         ace->aceWhoString[0] = '\0';
581         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
582         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
583         u.a.aclEntryN++;
584       }
585       {
586         ace->flags = ACE4_ID_SPECIAL;
587         ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
588         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
589         ace->aceFlags = 0;
590         ace->aceMask =
591           (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
592           | (mode & 0002
593              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
594                | ACE4_ADD_SUBDIRECTORY
595              : 0)
596           | (mode & 0001 ? ACE4_EXECUTE : 0);
597         ace->aceWhoString[0] = '\0';
598         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
599         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
600         u.a.aclEntryN++;
601       }
602       u.a.aclLength = (char *) ace - (char *) &u.a;
603
604       if (desc != -1)
605         ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
606                          type, &u.a, u.a.aclLength, mode);
607       else
608         ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
609                         type, &u.a, u.a.aclLength, mode);
610       if (!(ret < 0 && errno == ENOSYS))
611         return ret;
612     }
613
614   return chmod_or_fchmod (name, desc, mode);
615
616 # elif HAVE_STATACL /* older AIX */
617
618   union { struct acl a; char room[128]; } u;
619   int ret;
620
621   u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
622   u.a.acl_mode = mode & ~(S_IXACL | 0777);
623   u.a.u_access = (mode >> 6) & 7;
624   u.a.g_access = (mode >> 3) & 7;
625   u.a.o_access = mode & 7;
626
627   if (desc != -1)
628     ret = fchacl (desc, &u.a, u.a.acl_len);
629   else
630     ret = chacl (name, &u.a, u.a.acl_len);
631
632   if (ret < 0 && errno == ENOSYS)
633     return chmod_or_fchmod (name, desc, mode);
634
635   return ret;
636
637 # elif HAVE_ACLSORT /* NonStop Kernel */
638
639   struct acl entries[4];
640   int ret;
641
642   entries[0].a_type = USER_OBJ;
643   entries[0].a_id = 0; /* irrelevant */
644   entries[0].a_perm = (mode >> 6) & 7;
645   entries[1].a_type = GROUP_OBJ;
646   entries[1].a_id = 0; /* irrelevant */
647   entries[1].a_perm = (mode >> 3) & 7;
648   entries[2].a_type = CLASS_OBJ;
649   entries[2].a_id = 0;
650   entries[2].a_perm = (mode >> 3) & 7;
651   entries[3].a_type = OTHER_OBJ;
652   entries[3].a_id = 0;
653   entries[3].a_perm = mode & 7;
654
655   ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
656   if (ret > 0)
657     abort ();
658   if (ret < 0)
659     {
660       if (0)
661         return chmod_or_fchmod (name, desc, mode);
662       return -1;
663     }
664
665   ret = acl ((char *) name, ACL_SET,
666              sizeof (entries) / sizeof (struct acl), entries);
667   if (ret < 0)
668     {
669       if (0)
670         return chmod_or_fchmod (name, desc, mode);
671       return -1;
672     }
673
674   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
675     {
676       /* We did not call chmod so far, so the special bits have not yet
677          been set.  */
678       return chmod_or_fchmod (name, desc, mode);
679     }
680   return 0;
681
682 # else /* Unknown flavor of ACLs */
683   return chmod_or_fchmod (name, desc, mode);
684 # endif
685 #else /* !USE_ACL */
686   return chmod_or_fchmod (name, desc, mode);
687 #endif
688 }
689
690 /* As with qset_acl, but also output a diagnostic on failure.  */
691
692 int
693 set_acl (char const *name, int desc, mode_t mode)
694 {
695   int ret = qset_acl (name, desc, mode);
696   if (ret != 0)
697     error (0, errno, _("setting permissions for %s"), quote (name));
698   return ret;
699 }