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