be5592b38b8ad405053f33b09e10f268389f6d00
[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           entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
339           entries[0].a_flags = NEW_ACE_OWNER;
340           entries[0].a_who = 0; /* irrelevant */
341           entries[0].a_access_mask = 0;
342           entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
343           entries[1].a_flags = NEW_ACE_OWNER;
344           entries[1].a_who = 0; /* irrelevant */
345           entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
346                                      | NEW_ACE_WRITE_ATTRIBUTES
347                                      | NEW_ACE_WRITE_ACL
348                                      | NEW_ACE_WRITE_OWNER;
349           if (mode & 0400)
350             entries[1].a_access_mask |= NEW_ACE_READ_DATA;
351           else
352             entries[0].a_access_mask |= NEW_ACE_READ_DATA;
353           if (mode & 0200)
354             entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
355           else
356             entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
357           if (mode & 0100)
358             entries[1].a_access_mask |= NEW_ACE_EXECUTE;
359           else
360             entries[0].a_access_mask |= NEW_ACE_EXECUTE;
361           entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
362           entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
363           entries[2].a_who = 0; /* irrelevant */
364           entries[2].a_access_mask = 0;
365           entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
366           entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
367           entries[3].a_who = 0; /* irrelevant */
368           entries[3].a_access_mask = 0;
369           if (mode & 0040)
370             entries[3].a_access_mask |= NEW_ACE_READ_DATA;
371           else
372             entries[2].a_access_mask |= NEW_ACE_READ_DATA;
373           if (mode & 0020)
374             entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
375           else
376             entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
377           if (mode & 0010)
378             entries[3].a_access_mask |= NEW_ACE_EXECUTE;
379           else
380             entries[2].a_access_mask |= NEW_ACE_EXECUTE;
381           entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
382           entries[4].a_flags = ACE_EVERYONE;
383           entries[4].a_who = 0;
384           entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
385                                      | NEW_ACE_WRITE_ATTRIBUTES
386                                      | NEW_ACE_WRITE_ACL
387                                      | NEW_ACE_WRITE_OWNER;
388           entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
389           entries[5].a_flags = ACE_EVERYONE;
390           entries[5].a_who = 0;
391           entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
392                                      | NEW_ACE_READ_ATTRIBUTES
393                                      | NEW_ACE_READ_ACL
394                                      | NEW_ACE_SYNCHRONIZE;
395           if (mode & 0004)
396             entries[5].a_access_mask |= NEW_ACE_READ_DATA;
397           else
398             entries[4].a_access_mask |= NEW_ACE_READ_DATA;
399           if (mode & 0002)
400             entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
401           else
402             entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
403           if (mode & 0001)
404             entries[5].a_access_mask |= NEW_ACE_EXECUTE;
405           else
406             entries[4].a_access_mask |= NEW_ACE_EXECUTE;
407           count = 6;
408         }
409       if (desc != -1)
410         ret = facl (desc, ACE_SETACL, count, entries);
411       else
412         ret = acl (name, ACE_SETACL, count, entries);
413       if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
414         {
415           if (errno == ENOSYS)
416             return chmod_or_fchmod (name, desc, mode);
417           return -1;
418         }
419       if (ret == 0)
420         done_setacl = 1;
421     }
422 #   endif
423
424   if (!done_setacl)
425     {
426       aclent_t entries[3];
427       int ret;
428
429       entries[0].a_type = USER_OBJ;
430       entries[0].a_id = 0; /* irrelevant */
431       entries[0].a_perm = (mode >> 6) & 7;
432       entries[1].a_type = GROUP_OBJ;
433       entries[1].a_id = 0; /* irrelevant */
434       entries[1].a_perm = (mode >> 3) & 7;
435       entries[2].a_type = OTHER_OBJ;
436       entries[2].a_id = 0;
437       entries[2].a_perm = mode & 7;
438
439       if (desc != -1)
440         ret = facl (desc, SETACL,
441                     sizeof (entries) / sizeof (aclent_t), entries);
442       else
443         ret = acl (name, SETACL,
444                    sizeof (entries) / sizeof (aclent_t), entries);
445       if (ret < 0)
446         {
447           if (errno == ENOSYS || errno == EOPNOTSUPP)
448             return chmod_or_fchmod (name, desc, mode);
449           return -1;
450         }
451     }
452
453   if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
454     {
455       /* We did not call chmod so far, so the special bits have not yet
456          been set.  */
457       return chmod_or_fchmod (name, desc, mode);
458     }
459   return 0;
460
461 #  endif
462
463 # elif HAVE_GETACL /* HP-UX */
464
465   struct stat statbuf;
466   int ret;
467
468   if (desc != -1)
469     ret = fstat (desc, &statbuf);
470   else
471     ret = stat (name, &statbuf);
472   if (ret < 0)
473     return -1;
474
475   {
476     struct acl_entry entries[3];
477
478     entries[0].uid = statbuf.st_uid;
479     entries[0].gid = ACL_NSGROUP;
480     entries[0].mode = (mode >> 6) & 7;
481     entries[1].uid = ACL_NSUSER;
482     entries[1].gid = statbuf.st_gid;
483     entries[1].mode = (mode >> 3) & 7;
484     entries[2].uid = ACL_NSUSER;
485     entries[2].gid = ACL_NSGROUP;
486     entries[2].mode = mode & 7;
487
488     if (desc != -1)
489       ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
490     else
491       ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
492   }
493   if (ret < 0)
494     {
495       if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
496         return -1;
497
498 #  if HAVE_ACLV_H /* HP-UX >= 11.11 */
499       {
500         struct acl entries[4];
501
502         entries[0].a_type = USER_OBJ;
503         entries[0].a_id = 0; /* irrelevant */
504         entries[0].a_perm = (mode >> 6) & 7;
505         entries[1].a_type = GROUP_OBJ;
506         entries[1].a_id = 0; /* irrelevant */
507         entries[1].a_perm = (mode >> 3) & 7;
508         entries[2].a_type = CLASS_OBJ;
509         entries[2].a_id = 0;
510         entries[2].a_perm = (mode >> 3) & 7;
511         entries[3].a_type = OTHER_OBJ;
512         entries[3].a_id = 0;
513         entries[3].a_perm = mode & 7;
514
515         ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
516         if (ret > 0)
517           abort ();
518         if (ret < 0)
519           {
520             if (0)
521               return chmod_or_fchmod (name, desc, mode);
522             return -1;
523           }
524
525         ret = acl ((char *) name, ACL_SET,
526                    sizeof (entries) / sizeof (struct acl), entries);
527         if (ret < 0)
528           {
529             if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
530               return chmod_or_fchmod (name, desc, mode);
531             return -1;
532           }
533       }
534 #  else
535       return chmod_or_fchmod (name, desc, mode);
536 #  endif
537     }
538
539   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
540     {
541       /* We did not call chmod so far, so the special bits have not yet
542          been set.  */
543       return chmod_or_fchmod (name, desc, mode);
544     }
545   return 0;
546
547 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
548
549   acl_type_list_t types;
550   size_t types_size = sizeof (types);
551   acl_type_t type;
552
553   if (aclx_gettypes (name, &types, &types_size) < 0
554       || types.num_entries == 0)
555     return chmod_or_fchmod (name, desc, mode);
556
557   /* XXX Do we need to clear all types of ACLs for the given file, or is it
558      sufficient to clear the first one?  */
559   type = types.entries[0];
560   if (type.u64 == ACL_AIXC)
561     {
562       union { struct acl a; char room[128]; } u;
563       int ret;
564
565       u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
566       u.a.acl_mode = mode & ~(S_IXACL | 0777);
567       u.a.u_access = (mode >> 6) & 7;
568       u.a.g_access = (mode >> 3) & 7;
569       u.a.o_access = mode & 7;
570
571       if (desc != -1)
572         ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
573                          type, &u.a, u.a.acl_len, mode);
574       else
575         ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
576                         type, &u.a, u.a.acl_len, mode);
577       if (!(ret < 0 && errno == ENOSYS))
578         return ret;
579     }
580   else if (type.u64 == ACL_NFS4)
581     {
582       union { nfs4_acl_int_t a; char room[128]; } u;
583       nfs4_ace_int_t *ace;
584       int ret;
585
586       u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
587       u.a.aclEntryN = 0;
588       ace = &u.a.aclEntry[0];
589       {
590         ace->flags = ACE4_ID_SPECIAL;
591         ace->aceWho.special_whoid = ACE4_WHO_OWNER;
592         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
593         ace->aceFlags = 0;
594         ace->aceMask =
595           (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
596           | (mode & 0200
597              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
598                | ACE4_ADD_SUBDIRECTORY
599              : 0)
600           | (mode & 0100 ? ACE4_EXECUTE : 0);
601         ace->aceWhoString[0] = '\0';
602         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
603         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
604         u.a.aclEntryN++;
605       }
606       {
607         ace->flags = ACE4_ID_SPECIAL;
608         ace->aceWho.special_whoid = ACE4_WHO_GROUP;
609         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
610         ace->aceFlags = 0;
611         ace->aceMask =
612           (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
613           | (mode & 0020
614              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
615                | ACE4_ADD_SUBDIRECTORY
616              : 0)
617           | (mode & 0010 ? ACE4_EXECUTE : 0);
618         ace->aceWhoString[0] = '\0';
619         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
620         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
621         u.a.aclEntryN++;
622       }
623       {
624         ace->flags = ACE4_ID_SPECIAL;
625         ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
626         ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
627         ace->aceFlags = 0;
628         ace->aceMask =
629           (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
630           | (mode & 0002
631              ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
632                | ACE4_ADD_SUBDIRECTORY
633              : 0)
634           | (mode & 0001 ? ACE4_EXECUTE : 0);
635         ace->aceWhoString[0] = '\0';
636         ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
637         ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
638         u.a.aclEntryN++;
639       }
640       u.a.aclLength = (char *) ace - (char *) &u.a;
641
642       if (desc != -1)
643         ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
644                          type, &u.a, u.a.aclLength, mode);
645       else
646         ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
647                         type, &u.a, u.a.aclLength, mode);
648       if (!(ret < 0 && errno == ENOSYS))
649         return ret;
650     }
651
652   return chmod_or_fchmod (name, desc, mode);
653
654 # elif HAVE_STATACL /* older AIX */
655
656   union { struct acl a; char room[128]; } u;
657   int ret;
658
659   u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
660   u.a.acl_mode = mode & ~(S_IXACL | 0777);
661   u.a.u_access = (mode >> 6) & 7;
662   u.a.g_access = (mode >> 3) & 7;
663   u.a.o_access = mode & 7;
664
665   if (desc != -1)
666     ret = fchacl (desc, &u.a, u.a.acl_len);
667   else
668     ret = chacl (name, &u.a, u.a.acl_len);
669
670   if (ret < 0 && errno == ENOSYS)
671     return chmod_or_fchmod (name, desc, mode);
672
673   return ret;
674
675 # elif HAVE_ACLSORT /* NonStop Kernel */
676
677   struct acl entries[4];
678   int ret;
679
680   entries[0].a_type = USER_OBJ;
681   entries[0].a_id = 0; /* irrelevant */
682   entries[0].a_perm = (mode >> 6) & 7;
683   entries[1].a_type = GROUP_OBJ;
684   entries[1].a_id = 0; /* irrelevant */
685   entries[1].a_perm = (mode >> 3) & 7;
686   entries[2].a_type = CLASS_OBJ;
687   entries[2].a_id = 0;
688   entries[2].a_perm = (mode >> 3) & 7;
689   entries[3].a_type = OTHER_OBJ;
690   entries[3].a_id = 0;
691   entries[3].a_perm = mode & 7;
692
693   ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
694   if (ret > 0)
695     abort ();
696   if (ret < 0)
697     {
698       if (0)
699         return chmod_or_fchmod (name, desc, mode);
700       return -1;
701     }
702
703   ret = acl ((char *) name, ACL_SET,
704              sizeof (entries) / sizeof (struct acl), entries);
705   if (ret < 0)
706     {
707       if (0)
708         return chmod_or_fchmod (name, desc, mode);
709       return -1;
710     }
711
712   if (mode & (S_ISUID | S_ISGID | S_ISVTX))
713     {
714       /* We did not call chmod so far, so the special bits have not yet
715          been set.  */
716       return chmod_or_fchmod (name, desc, mode);
717     }
718   return 0;
719
720 # else /* Unknown flavor of ACLs */
721   return chmod_or_fchmod (name, desc, mode);
722 # endif
723 #else /* !USE_ACL */
724   return chmod_or_fchmod (name, desc, mode);
725 #endif
726 }
727
728 /* As with qset_acl, but also output a diagnostic on failure.  */
729
730 int
731 set_acl (char const *name, int desc, mode_t mode)
732 {
733   int r = qset_acl (name, desc, mode);
734   if (r != 0)
735     error (0, errno, _("setting permissions for %s"), quote (name));
736   return r;
737 }