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