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