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