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