Merge commit 'b572c3a256e7bf1e4eecc8c36448c08093240a6a' into stable
[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-2011 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, MacOS 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   /* MacOS X */
145
146   /* On MacOS 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 GETACLCNT /* 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     int count;
218     ace_t *entries;
219
220     for (;;)
221       {
222         if (desc != -1)
223           count = facl (desc, ACE_GETACLCNT, 0, NULL);
224         else
225           count = acl (name, ACE_GETACLCNT, 0, NULL);
226         if (count <= 0)
227           {
228             convention = -1;
229             break;
230           }
231         entries = (ace_t *) malloc (count * sizeof (ace_t));
232         if (entries == NULL)
233           {
234             errno = ENOMEM;
235             return -1;
236           }
237         if ((desc != -1
238              ? facl (desc, ACE_GETACL, count, entries)
239              : acl (name, ACE_GETACL, count, entries))
240             == count)
241           {
242             int i;
243
244             convention = 0;
245             for (i = 0; i < count; i++)
246               if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
247                 {
248                   convention = 1;
249                   break;
250                 }
251             free (entries);
252             break;
253           }
254         /* Huh? The number of ACL entries changed since the last call.
255            Repeat.  */
256         free (entries);
257       }
258   }
259
260   if (convention >= 0)
261     {
262       ace_t entries[6];
263       int count;
264       int ret;
265
266       if (convention)
267         {
268           /* Running on Solaris 10.  */
269           entries[0].a_type = OLD_ALLOW;
270           entries[0].a_flags = OLD_ACE_OWNER;
271           entries[0].a_who = 0; /* irrelevant */
272           entries[0].a_access_mask = (mode >> 6) & 7;
273           entries[1].a_type = OLD_ALLOW;
274           entries[1].a_flags = OLD_ACE_GROUP;
275           entries[1].a_who = 0; /* irrelevant */
276           entries[1].a_access_mask = (mode >> 3) & 7;
277           entries[2].a_type = OLD_ALLOW;
278           entries[2].a_flags = OLD_ACE_OTHER;
279           entries[2].a_who = 0;
280           entries[2].a_access_mask = mode & 7;
281           count = 3;
282         }
283       else
284         {
285           /* Running on Solaris 10 (newer version) or Solaris 11.
286              The details here were found through "/bin/ls -lvd somefiles".  */
287           entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
288           entries[0].a_flags = NEW_ACE_OWNER;
289           entries[0].a_who = 0; /* irrelevant */
290           entries[0].a_access_mask = 0;
291           entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
292           entries[1].a_flags = NEW_ACE_OWNER;
293           entries[1].a_who = 0; /* irrelevant */
294           entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
295                                      | NEW_ACE_WRITE_ATTRIBUTES
296                                      | NEW_ACE_WRITE_ACL
297                                      | NEW_ACE_WRITE_OWNER;
298           if (mode & 0400)
299             entries[1].a_access_mask |= NEW_ACE_READ_DATA;
300           else
301             entries[0].a_access_mask |= NEW_ACE_READ_DATA;
302           if (mode & 0200)
303             entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
304           else
305             entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
306           if (mode & 0100)
307             entries[1].a_access_mask |= NEW_ACE_EXECUTE;
308           else
309             entries[0].a_access_mask |= NEW_ACE_EXECUTE;
310           entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
311           entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
312           entries[2].a_who = 0; /* irrelevant */
313           entries[2].a_access_mask = 0;
314           entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
315           entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
316           entries[3].a_who = 0; /* irrelevant */
317           entries[3].a_access_mask = 0;
318           if (mode & 0040)
319             entries[3].a_access_mask |= NEW_ACE_READ_DATA;
320           else
321             entries[2].a_access_mask |= NEW_ACE_READ_DATA;
322           if (mode & 0020)
323             entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
324           else
325             entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
326           if (mode & 0010)
327             entries[3].a_access_mask |= NEW_ACE_EXECUTE;
328           else
329             entries[2].a_access_mask |= NEW_ACE_EXECUTE;
330           entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
331           entries[4].a_flags = NEW_ACE_EVERYONE;
332           entries[4].a_who = 0;
333           entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
334                                      | NEW_ACE_WRITE_ATTRIBUTES
335                                      | NEW_ACE_WRITE_ACL
336                                      | NEW_ACE_WRITE_OWNER;
337           entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
338           entries[5].a_flags = NEW_ACE_EVERYONE;
339           entries[5].a_who = 0;
340           entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
341                                      | NEW_ACE_READ_ATTRIBUTES
342                                      | NEW_ACE_READ_ACL
343                                      | NEW_ACE_SYNCHRONIZE;
344           if (mode & 0004)
345             entries[5].a_access_mask |= NEW_ACE_READ_DATA;
346           else
347             entries[4].a_access_mask |= NEW_ACE_READ_DATA;
348           if (mode & 0002)
349             entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
350           else
351             entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
352           if (mode & 0001)
353             entries[5].a_access_mask |= NEW_ACE_EXECUTE;
354           else
355             entries[4].a_access_mask |= NEW_ACE_EXECUTE;
356           count = 6;
357         }
358       if (desc != -1)
359         ret = facl (desc, ACE_SETACL, count, entries);
360       else
361         ret = acl (name, ACE_SETACL, count, entries);
362       if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
363         {
364           if (errno == ENOSYS)
365             return chmod_or_fchmod (name, desc, mode);
366           return -1;
367         }
368       if (ret == 0)
369         done_setacl = 1;
370     }
371 #  endif
372
373   if (!done_setacl)
374     {
375       aclent_t entries[3];
376       int ret;
377
378       entries[0].a_type = USER_OBJ;
379       entries[0].a_id = 0; /* irrelevant */
380       entries[0].a_perm = (mode >> 6) & 7;
381       entries[1].a_type = GROUP_OBJ;
382       entries[1].a_id = 0; /* irrelevant */
383       entries[1].a_perm = (mode >> 3) & 7;
384       entries[2].a_type = OTHER_OBJ;
385       entries[2].a_id = 0;
386       entries[2].a_perm = mode & 7;
387
388       if (desc != -1)
389         ret = facl (desc, SETACL,
390                     sizeof (entries) / sizeof (aclent_t), entries);
391       else
392         ret = acl (name, SETACL,
393                    sizeof (entries) / sizeof (aclent_t), entries);
394       if (ret < 0)
395         {
396           if (errno == ENOSYS || errno == EOPNOTSUPP)
397             return chmod_or_fchmod (name, desc, mode);
398           return -1;
399         }
400     }
401
402   if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
403     {
404       /* We did not call chmod so far, so the special bits have not yet
405          been set.  */
406       return chmod_or_fchmod (name, desc, mode);
407     }
408   return 0;
409
410 # elif HAVE_GETACL /* HP-UX */
411
412   struct stat statbuf;
413   int ret;
414
415   if (desc != -1)
416     ret = fstat (desc, &statbuf);
417   else
418     ret = stat (name, &statbuf);
419   if (ret < 0)
420     return -1;
421
422   {
423     struct acl_entry entries[3];
424
425     entries[0].uid = statbuf.st_uid;
426     entries[0].gid = ACL_NSGROUP;
427     entries[0].mode = (mode >> 6) & 7;
428     entries[1].uid = ACL_NSUSER;
429     entries[1].gid = statbuf.st_gid;
430     entries[1].mode = (mode >> 3) & 7;
431     entries[2].uid = ACL_NSUSER;
432     entries[2].gid = ACL_NSGROUP;
433     entries[2].mode = mode & 7;
434
435     if (desc != -1)
436       ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
437     else
438       ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
439   }
440   if (ret < 0)
441     {
442       if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
443         return -1;
444
445 #  if HAVE_ACLV_H /* HP-UX >= 11.11 */
446       {
447         struct acl entries[4];
448
449         entries[0].a_type = USER_OBJ;
450         entries[0].a_id = 0; /* irrelevant */
451         entries[0].a_perm = (mode >> 6) & 7;
452         entries[1].a_type = GROUP_OBJ;
453         entries[1].a_id = 0; /* irrelevant */
454         entries[1].a_perm = (mode >> 3) & 7;
455         entries[2].a_type = CLASS_OBJ;
456         entries[2].a_id = 0;
457         entries[2].a_perm = (mode >> 3) & 7;
458         entries[3].a_type = OTHER_OBJ;
459         entries[3].a_id = 0;
460         entries[3].a_perm = mode & 7;
461
462         ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
463         if (ret > 0)
464           abort ();
465         if (ret < 0)
466           {
467             if (0)
468               return chmod_or_fchmod (name, desc, mode);
469             return -1;
470           }
471
472         ret = acl ((char *) name, ACL_SET,
473                    sizeof (entries) / sizeof (struct acl), entries);
474         if (ret < 0)
475           {
476             if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
477               return chmod_or_fchmod (name, desc, mode);
478             return -1;
479           }
480       }
481 #  else
482       return chmod_or_fchmod (name, desc, mode);
483 #  endif
484     }
485
486   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
487     {
488       /* We did not call chmod so far, so the special bits have not yet
489          been set.  */
490       return chmod_or_fchmod (name, desc, mode);
491     }
492   return 0;
493
494 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
495
496   acl_type_list_t types;
497   size_t types_size = sizeof (types);
498   acl_type_t type;
499
500   if (aclx_gettypes (name, &types, &types_size) < 0
501       || types.num_entries == 0)
502     return chmod_or_fchmod (name, desc, mode);
503
504   /* XXX Do we need to clear all types of ACLs for the given file, or is it
505      sufficient to clear the first one?  */
506   type = types.entries[0];
507   if (type.u64 == ACL_AIXC)
508     {
509       union { struct acl a; char room[128]; } u;
510       int ret;
511
512       u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
513       u.a.acl_mode = mode & ~(S_IXACL | 0777);
514       u.a.u_access = (mode >> 6) & 7;
515       u.a.g_access = (mode >> 3) & 7;
516       u.a.o_access = mode & 7;
517
518       if (desc != -1)
519         ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
520                          type, &u.a, u.a.acl_len, mode);
521       else
522         ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
523                         type, &u.a, u.a.acl_len, mode);
524       if (!(ret < 0 && errno == ENOSYS))
525         return ret;
526     }
527   else if (type.u64 == ACL_NFS4)
528     {
529       union { nfs4_acl_int_t a; char room[128]; } u;
530       nfs4_ace_int_t *ace;
531       int ret;
532
533       u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
534       u.a.aclEntryN = 0;
535       ace = &u.a.aclEntry[0];
536       {
537         ace->flags = ACE4_ID_SPECIAL;
538         ace->aceWho.special_whoid = ACE4_WHO_OWNER;
539         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
540         ace->aceFlags = 0;
541         ace->aceMask =
542           (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
543           | (mode & 0200
544              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
545                | ACE4_ADD_SUBDIRECTORY
546              : 0)
547           | (mode & 0100 ? ACE4_EXECUTE : 0);
548         ace->aceWhoString[0] = '\0';
549         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
550         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
551         u.a.aclEntryN++;
552       }
553       {
554         ace->flags = ACE4_ID_SPECIAL;
555         ace->aceWho.special_whoid = ACE4_WHO_GROUP;
556         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
557         ace->aceFlags = 0;
558         ace->aceMask =
559           (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
560           | (mode & 0020
561              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
562                | ACE4_ADD_SUBDIRECTORY
563              : 0)
564           | (mode & 0010 ? ACE4_EXECUTE : 0);
565         ace->aceWhoString[0] = '\0';
566         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
567         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
568         u.a.aclEntryN++;
569       }
570       {
571         ace->flags = ACE4_ID_SPECIAL;
572         ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
573         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
574         ace->aceFlags = 0;
575         ace->aceMask =
576           (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
577           | (mode & 0002
578              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
579                | ACE4_ADD_SUBDIRECTORY
580              : 0)
581           | (mode & 0001 ? ACE4_EXECUTE : 0);
582         ace->aceWhoString[0] = '\0';
583         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
584         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
585         u.a.aclEntryN++;
586       }
587       u.a.aclLength = (char *) ace - (char *) &u.a;
588
589       if (desc != -1)
590         ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
591                          type, &u.a, u.a.aclLength, mode);
592       else
593         ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
594                         type, &u.a, u.a.aclLength, mode);
595       if (!(ret < 0 && errno == ENOSYS))
596         return ret;
597     }
598
599   return chmod_or_fchmod (name, desc, mode);
600
601 # elif HAVE_STATACL /* older AIX */
602
603   union { struct acl a; char room[128]; } u;
604   int ret;
605
606   u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
607   u.a.acl_mode = mode & ~(S_IXACL | 0777);
608   u.a.u_access = (mode >> 6) & 7;
609   u.a.g_access = (mode >> 3) & 7;
610   u.a.o_access = mode & 7;
611
612   if (desc != -1)
613     ret = fchacl (desc, &u.a, u.a.acl_len);
614   else
615     ret = chacl (name, &u.a, u.a.acl_len);
616
617   if (ret < 0 && errno == ENOSYS)
618     return chmod_or_fchmod (name, desc, mode);
619
620   return ret;
621
622 # elif HAVE_ACLSORT /* NonStop Kernel */
623
624   struct acl entries[4];
625   int ret;
626
627   entries[0].a_type = USER_OBJ;
628   entries[0].a_id = 0; /* irrelevant */
629   entries[0].a_perm = (mode >> 6) & 7;
630   entries[1].a_type = GROUP_OBJ;
631   entries[1].a_id = 0; /* irrelevant */
632   entries[1].a_perm = (mode >> 3) & 7;
633   entries[2].a_type = CLASS_OBJ;
634   entries[2].a_id = 0;
635   entries[2].a_perm = (mode >> 3) & 7;
636   entries[3].a_type = OTHER_OBJ;
637   entries[3].a_id = 0;
638   entries[3].a_perm = mode & 7;
639
640   ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
641   if (ret > 0)
642     abort ();
643   if (ret < 0)
644     {
645       if (0)
646         return chmod_or_fchmod (name, desc, mode);
647       return -1;
648     }
649
650   ret = acl ((char *) name, ACL_SET,
651              sizeof (entries) / sizeof (struct acl), entries);
652   if (ret < 0)
653     {
654       if (0)
655         return chmod_or_fchmod (name, desc, mode);
656       return -1;
657     }
658
659   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
660     {
661       /* We did not call chmod so far, so the special bits have not yet
662          been set.  */
663       return chmod_or_fchmod (name, desc, mode);
664     }
665   return 0;
666
667 # else /* Unknown flavor of ACLs */
668   return chmod_or_fchmod (name, desc, mode);
669 # endif
670 #else /* !USE_ACL */
671   return chmod_or_fchmod (name, desc, mode);
672 #endif
673 }
674
675 /* As with qset_acl, but also output a diagnostic on failure.  */
676
677 int
678 set_acl (char const *name, int desc, mode_t mode)
679 {
680   int r = qset_acl (name, desc, mode);
681   if (r != 0)
682     error (0, errno, _("setting permissions for %s"), quote (name));
683   return r;
684 }