1 /* set-mode-acl.c - set access control list equivalent to a mode
3 Copyright (C) 2002-2003, 2005-2013 Free Software Foundation, Inc.
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.
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.
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/>.
18 Written by Paul Eggert and Andreas Gruenbacher, and Bruno Haible. */
22 #define ACL_INTERNAL_INLINE _GL_EXTERN_INLINE
26 #include "acl-internal.h"
29 #define _(msgid) gettext (msgid)
32 /* If DESC is a valid file descriptor use fchmod to change the
33 file's mode to MODE on systems that have fchown. On systems
34 that don't have fchown and if DESC is invalid, use chown on
36 Return 0 if successful. Return -1 and set errno upon failure. */
39 chmod_or_fchmod (const char *name, int desc, mode_t mode)
41 if (HAVE_FCHMOD && desc != -1)
42 return fchmod (desc, mode);
44 return chmod (name, mode);
47 /* Set the access control lists of a file. If DESC is a valid file
48 descriptor, use file descriptor operations where available, else use
49 filename based operations on NAME. If access control lists are not
50 available, fchmod the target file to MODE. Also sets the
51 non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX)
52 to those from MODE if any are set.
53 Return 0 if successful. Return -1 and set errno upon failure. */
56 qset_acl (char const *name, int desc, mode_t mode)
59 # if HAVE_ACL_GET_FILE
60 /* POSIX 1003.1e draft 17 (abandoned) specific version. */
61 /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
62 # if !HAVE_ACL_TYPE_EXTENDED
63 /* Linux, FreeBSD, IRIX, Tru64 */
65 /* We must also have acl_from_text and acl_delete_def_file.
66 (acl_delete_def_file could be emulated with acl_init followed
67 by acl_set_file, but acl_set_file with an empty acl is
70 # ifndef HAVE_ACL_FROM_TEXT
71 # error Must have acl_from_text (see POSIX 1003.1e draft 17).
73 # ifndef HAVE_ACL_DELETE_DEF_FILE
74 # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
80 if (HAVE_ACL_FROM_MODE) /* Linux */
82 acl = acl_from_mode (mode);
86 else /* FreeBSD, IRIX, Tru64 */
88 /* If we were to create the ACL using the functions acl_init(),
89 acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(),
90 acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we
91 would need to create a qualifier. I don't know how to do this.
92 So create it using acl_from_text(). */
94 # if HAVE_ACL_FREE_TEXT /* Tru64 */
95 char acl_text[] = "u::---,g::---,o::---,";
96 # else /* FreeBSD, IRIX */
97 char acl_text[] = "u::---,g::---,o::---";
100 if (mode & S_IRUSR) acl_text[ 3] = 'r';
101 if (mode & S_IWUSR) acl_text[ 4] = 'w';
102 if (mode & S_IXUSR) acl_text[ 5] = 'x';
103 if (mode & S_IRGRP) acl_text[10] = 'r';
104 if (mode & S_IWGRP) acl_text[11] = 'w';
105 if (mode & S_IXGRP) acl_text[12] = 'x';
106 if (mode & S_IROTH) acl_text[17] = 'r';
107 if (mode & S_IWOTH) acl_text[18] = 'w';
108 if (mode & S_IXOTH) acl_text[19] = 'x';
110 acl = acl_from_text (acl_text);
114 if (HAVE_ACL_SET_FD && desc != -1)
115 ret = acl_set_fd (desc, acl);
117 ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
120 int saved_errno = errno;
123 if (ACL_NOT_WELL_SUPPORTED (errno))
124 return chmod_or_fchmod (name, desc, mode);
134 if (S_ISDIR (mode) && acl_delete_def_file (name))
137 if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
139 /* We did not call chmod so far, and either the mode and the ACL are
140 separate or special bits are to be set which don't fit into ACLs. */
141 return chmod_or_fchmod (name, desc, mode);
145 # else /* HAVE_ACL_TYPE_EXTENDED */
148 /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
149 and acl_get_file (name, ACL_TYPE_DEFAULT)
150 always return NULL / EINVAL. You have to use
151 acl_get_file (name, ACL_TYPE_EXTENDED)
152 or acl_get_fd (open (name, ...))
155 acl_set_file (name, ACL_TYPE_ACCESS, acl)
156 and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
157 have the same effect as
158 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
159 Each of these calls sets the file's ACL. */
164 /* Remove the ACL if the file has ACLs. */
165 if (HAVE_ACL_GET_FD && desc != -1)
166 acl = acl_get_fd (desc);
168 acl = acl_get_file (name, ACL_TYPE_EXTENDED);
176 if (HAVE_ACL_SET_FD && desc != -1)
177 ret = acl_set_fd (desc, acl);
179 ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
182 int saved_errno = errno;
186 if (ACL_NOT_WELL_SUPPORTED (saved_errno))
187 return chmod_or_fchmod (name, desc, mode);
198 /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
199 return chmod_or_fchmod (name, desc, mode);
202 # elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
207 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
208 file systems (whereas the other ones are used in UFS file systems). */
210 /* The flags in the ace_t structure changed in a binary incompatible way
211 when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
212 How to distinguish the two conventions at runtime?
213 We fetch the existing ACL. In the old convention, usually three ACEs have
214 a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
215 In the new convention, these values are not used. */
219 /* Initially, try to read the entries into a stack-allocated buffer.
220 Use malloc if it does not fit. */
223 alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
224 alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
226 ace_t buf[alloc_init];
227 size_t alloc = alloc_init;
228 ace_t *entries = buf;
229 ace_t *malloced = NULL;
235 ? facl (desc, ACE_GETACL, alloc, entries)
236 : acl (name, ACE_GETACL, alloc, entries));
237 if (count < 0 && errno == ENOSPC)
239 /* Increase the size of the buffer. */
241 if (alloc > alloc_max / 2)
246 alloc = 2 * alloc; /* <= alloc_max */
247 entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
265 for (i = 0; i < count; i++)
266 if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
283 /* Running on Solaris 10. */
284 entries[0].a_type = OLD_ALLOW;
285 entries[0].a_flags = OLD_ACE_OWNER;
286 entries[0].a_who = 0; /* irrelevant */
287 entries[0].a_access_mask = (mode >> 6) & 7;
288 entries[1].a_type = OLD_ALLOW;
289 entries[1].a_flags = OLD_ACE_GROUP;
290 entries[1].a_who = 0; /* irrelevant */
291 entries[1].a_access_mask = (mode >> 3) & 7;
292 entries[2].a_type = OLD_ALLOW;
293 entries[2].a_flags = OLD_ACE_OTHER;
294 entries[2].a_who = 0;
295 entries[2].a_access_mask = mode & 7;
300 /* Running on Solaris 10 (newer version) or Solaris 11.
301 The details here were found through "/bin/ls -lvd somefiles". */
302 entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
303 entries[0].a_flags = NEW_ACE_OWNER;
304 entries[0].a_who = 0; /* irrelevant */
305 entries[0].a_access_mask = 0;
306 entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
307 entries[1].a_flags = NEW_ACE_OWNER;
308 entries[1].a_who = 0; /* irrelevant */
309 entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
310 | NEW_ACE_WRITE_ATTRIBUTES
312 | NEW_ACE_WRITE_OWNER;
314 entries[1].a_access_mask |= NEW_ACE_READ_DATA;
316 entries[0].a_access_mask |= NEW_ACE_READ_DATA;
318 entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
320 entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
322 entries[1].a_access_mask |= NEW_ACE_EXECUTE;
324 entries[0].a_access_mask |= NEW_ACE_EXECUTE;
325 entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
326 entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
327 entries[2].a_who = 0; /* irrelevant */
328 entries[2].a_access_mask = 0;
329 entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
330 entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
331 entries[3].a_who = 0; /* irrelevant */
332 entries[3].a_access_mask = 0;
334 entries[3].a_access_mask |= NEW_ACE_READ_DATA;
336 entries[2].a_access_mask |= NEW_ACE_READ_DATA;
338 entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
340 entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
342 entries[3].a_access_mask |= NEW_ACE_EXECUTE;
344 entries[2].a_access_mask |= NEW_ACE_EXECUTE;
345 entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
346 entries[4].a_flags = NEW_ACE_EVERYONE;
347 entries[4].a_who = 0;
348 entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
349 | NEW_ACE_WRITE_ATTRIBUTES
351 | NEW_ACE_WRITE_OWNER;
352 entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
353 entries[5].a_flags = NEW_ACE_EVERYONE;
354 entries[5].a_who = 0;
355 entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
356 | NEW_ACE_READ_ATTRIBUTES
358 | NEW_ACE_SYNCHRONIZE;
360 entries[5].a_access_mask |= NEW_ACE_READ_DATA;
362 entries[4].a_access_mask |= NEW_ACE_READ_DATA;
364 entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
366 entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
368 entries[5].a_access_mask |= NEW_ACE_EXECUTE;
370 entries[4].a_access_mask |= NEW_ACE_EXECUTE;
374 ret = facl (desc, ACE_SETACL, count, entries);
376 ret = acl (name, ACE_SETACL, count, entries);
377 if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
380 return chmod_or_fchmod (name, desc, mode);
393 entries[0].a_type = USER_OBJ;
394 entries[0].a_id = 0; /* irrelevant */
395 entries[0].a_perm = (mode >> 6) & 7;
396 entries[1].a_type = GROUP_OBJ;
397 entries[1].a_id = 0; /* irrelevant */
398 entries[1].a_perm = (mode >> 3) & 7;
399 entries[2].a_type = OTHER_OBJ;
401 entries[2].a_perm = mode & 7;
404 ret = facl (desc, SETACL,
405 sizeof (entries) / sizeof (aclent_t), entries);
407 ret = acl (name, SETACL,
408 sizeof (entries) / sizeof (aclent_t), entries);
411 if (errno == ENOSYS || errno == EOPNOTSUPP)
412 return chmod_or_fchmod (name, desc, mode);
417 if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
419 /* We did not call chmod so far, so the special bits have not yet
421 return chmod_or_fchmod (name, desc, mode);
425 # elif HAVE_GETACL /* HP-UX */
431 ret = fstat (desc, &statbuf);
433 ret = stat (name, &statbuf);
438 struct acl_entry entries[3];
440 entries[0].uid = statbuf.st_uid;
441 entries[0].gid = ACL_NSGROUP;
442 entries[0].mode = (mode >> 6) & 7;
443 entries[1].uid = ACL_NSUSER;
444 entries[1].gid = statbuf.st_gid;
445 entries[1].mode = (mode >> 3) & 7;
446 entries[2].uid = ACL_NSUSER;
447 entries[2].gid = ACL_NSGROUP;
448 entries[2].mode = mode & 7;
451 ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
453 ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
457 if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
460 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
462 struct acl entries[4];
464 entries[0].a_type = USER_OBJ;
465 entries[0].a_id = 0; /* irrelevant */
466 entries[0].a_perm = (mode >> 6) & 7;
467 entries[1].a_type = GROUP_OBJ;
468 entries[1].a_id = 0; /* irrelevant */
469 entries[1].a_perm = (mode >> 3) & 7;
470 entries[2].a_type = CLASS_OBJ;
472 entries[2].a_perm = (mode >> 3) & 7;
473 entries[3].a_type = OTHER_OBJ;
475 entries[3].a_perm = mode & 7;
477 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
483 return chmod_or_fchmod (name, desc, mode);
487 ret = acl ((char *) name, ACL_SET,
488 sizeof (entries) / sizeof (struct acl), entries);
491 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
492 return chmod_or_fchmod (name, desc, mode);
497 return chmod_or_fchmod (name, desc, mode);
501 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
503 /* We did not call chmod so far, so the special bits have not yet
505 return chmod_or_fchmod (name, desc, mode);
509 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
511 acl_type_list_t types;
512 size_t types_size = sizeof (types);
515 if (aclx_gettypes (name, &types, &types_size) < 0
516 || types.num_entries == 0)
517 return chmod_or_fchmod (name, desc, mode);
519 /* XXX Do we need to clear all types of ACLs for the given file, or is it
520 sufficient to clear the first one? */
521 type = types.entries[0];
522 if (type.u64 == ACL_AIXC)
524 union { struct acl a; char room[128]; } u;
527 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
528 u.a.acl_mode = mode & ~(S_IXACL | 0777);
529 u.a.u_access = (mode >> 6) & 7;
530 u.a.g_access = (mode >> 3) & 7;
531 u.a.o_access = mode & 7;
534 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
535 type, &u.a, u.a.acl_len, mode);
537 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
538 type, &u.a, u.a.acl_len, mode);
539 if (!(ret < 0 && errno == ENOSYS))
542 else if (type.u64 == ACL_NFS4)
544 union { nfs4_acl_int_t a; char room[128]; } u;
548 u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
550 ace = &u.a.aclEntry[0];
552 ace->flags = ACE4_ID_SPECIAL;
553 ace->aceWho.special_whoid = ACE4_WHO_OWNER;
554 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
557 (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
559 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
560 | ACE4_ADD_SUBDIRECTORY
562 | (mode & 0100 ? ACE4_EXECUTE : 0);
563 ace->aceWhoString[0] = '\0';
564 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
565 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
569 ace->flags = ACE4_ID_SPECIAL;
570 ace->aceWho.special_whoid = ACE4_WHO_GROUP;
571 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
574 (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
576 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
577 | ACE4_ADD_SUBDIRECTORY
579 | (mode & 0010 ? ACE4_EXECUTE : 0);
580 ace->aceWhoString[0] = '\0';
581 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
582 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
586 ace->flags = ACE4_ID_SPECIAL;
587 ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
588 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
591 (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
593 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
594 | ACE4_ADD_SUBDIRECTORY
596 | (mode & 0001 ? ACE4_EXECUTE : 0);
597 ace->aceWhoString[0] = '\0';
598 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
599 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
602 u.a.aclLength = (char *) ace - (char *) &u.a;
605 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
606 type, &u.a, u.a.aclLength, mode);
608 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
609 type, &u.a, u.a.aclLength, mode);
610 if (!(ret < 0 && errno == ENOSYS))
614 return chmod_or_fchmod (name, desc, mode);
616 # elif HAVE_STATACL /* older AIX */
618 union { struct acl a; char room[128]; } u;
621 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
622 u.a.acl_mode = mode & ~(S_IXACL | 0777);
623 u.a.u_access = (mode >> 6) & 7;
624 u.a.g_access = (mode >> 3) & 7;
625 u.a.o_access = mode & 7;
628 ret = fchacl (desc, &u.a, u.a.acl_len);
630 ret = chacl (name, &u.a, u.a.acl_len);
632 if (ret < 0 && errno == ENOSYS)
633 return chmod_or_fchmod (name, desc, mode);
637 # elif HAVE_ACLSORT /* NonStop Kernel */
639 struct acl entries[4];
642 entries[0].a_type = USER_OBJ;
643 entries[0].a_id = 0; /* irrelevant */
644 entries[0].a_perm = (mode >> 6) & 7;
645 entries[1].a_type = GROUP_OBJ;
646 entries[1].a_id = 0; /* irrelevant */
647 entries[1].a_perm = (mode >> 3) & 7;
648 entries[2].a_type = CLASS_OBJ;
650 entries[2].a_perm = (mode >> 3) & 7;
651 entries[3].a_type = OTHER_OBJ;
653 entries[3].a_perm = mode & 7;
655 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
661 return chmod_or_fchmod (name, desc, mode);
665 ret = acl ((char *) name, ACL_SET,
666 sizeof (entries) / sizeof (struct acl), entries);
670 return chmod_or_fchmod (name, desc, mode);
674 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
676 /* We did not call chmod so far, so the special bits have not yet
678 return chmod_or_fchmod (name, desc, mode);
682 # else /* Unknown flavor of ACLs */
683 return chmod_or_fchmod (name, desc, mode);
686 return chmod_or_fchmod (name, desc, mode);
690 /* As with qset_acl, but also output a diagnostic on failure. */
693 set_acl (char const *name, int desc, mode_t mode)
695 int ret = qset_acl (name, desc, mode);
697 error (0, errno, _("setting permissions for %s"), quote (name));