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