1 /* set-mode-acl.c - set access control list equivalent to a mode
3 Copyright (C) 2002-2003, 2005-2011 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. */
24 #include "acl-internal.h"
27 #define _(msgid) gettext (msgid)
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
34 Return 0 if successful. Return -1 and set errno upon failure. */
37 chmod_or_fchmod (const char *name, int desc, mode_t mode)
39 if (HAVE_FCHMOD && desc != -1)
40 return fchmod (desc, mode);
42 return chmod (name, mode);
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. */
54 qset_acl (char const *name, int desc, mode_t mode)
57 # if HAVE_ACL_GET_FILE
58 /* POSIX 1003.1e draft 17 (abandoned) specific version. */
59 /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
60 # if !HAVE_ACL_TYPE_EXTENDED
61 /* Linux, FreeBSD, IRIX, Tru64 */
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
68 # ifndef HAVE_ACL_FROM_TEXT
69 # error Must have acl_from_text (see POSIX 1003.1e draft 17).
71 # ifndef HAVE_ACL_DELETE_DEF_FILE
72 # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
78 if (HAVE_ACL_FROM_MODE) /* Linux */
80 acl = acl_from_mode (mode);
84 else /* FreeBSD, IRIX, Tru64 */
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(). */
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::---";
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';
108 acl = acl_from_text (acl_text);
112 if (HAVE_ACL_SET_FD && desc != -1)
113 ret = acl_set_fd (desc, acl);
115 ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
118 int saved_errno = errno;
121 if (ACL_NOT_WELL_SUPPORTED (errno))
122 return chmod_or_fchmod (name, desc, mode);
132 if (S_ISDIR (mode) && acl_delete_def_file (name))
135 if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
137 /* We did not call chmod so far, and either the mode and the ACL are
138 separate or special bits are to be set which don't fit into ACLs. */
139 return chmod_or_fchmod (name, desc, mode);
143 # else /* HAVE_ACL_TYPE_EXTENDED */
146 /* On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
147 and acl_get_file (name, ACL_TYPE_DEFAULT)
148 always return NULL / EINVAL. You have to use
149 acl_get_file (name, ACL_TYPE_EXTENDED)
150 or acl_get_fd (open (name, ...))
153 acl_set_file (name, ACL_TYPE_ACCESS, acl)
154 and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
155 have the same effect as
156 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
157 Each of these calls sets the file's ACL. */
162 /* Remove the ACL if the file has ACLs. */
163 if (HAVE_ACL_GET_FD && desc != -1)
164 acl = acl_get_fd (desc);
166 acl = acl_get_file (name, ACL_TYPE_EXTENDED);
174 if (HAVE_ACL_SET_FD && desc != -1)
175 ret = acl_set_fd (desc, acl);
177 ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
180 int saved_errno = errno;
184 if (ACL_NOT_WELL_SUPPORTED (saved_errno))
185 return chmod_or_fchmod (name, desc, mode);
196 /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
197 return chmod_or_fchmod (name, desc, mode);
200 # elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
205 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
206 file systems (whereas the other ones are used in UFS file systems). */
208 /* The flags in the ace_t structure changed in a binary incompatible way
209 when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
210 How to distinguish the two conventions at runtime?
211 We fetch the existing ACL. In the old convention, usually three ACEs have
212 a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
213 In the new convention, these values are not used. */
223 count = facl (desc, ACE_GETACLCNT, 0, NULL);
225 count = acl (name, ACE_GETACLCNT, 0, NULL);
231 entries = (ace_t *) malloc (count * sizeof (ace_t));
238 ? facl (desc, ACE_GETACL, count, entries)
239 : acl (name, ACE_GETACL, count, entries))
245 for (i = 0; i < count; i++)
246 if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
254 /* Huh? The number of ACL entries changed since the last call.
268 /* Running on Solaris 10. */
269 entries[0].a_type = OLD_ALLOW;
270 entries[0].a_flags = OLD_ACE_OWNER;
271 entries[0].a_who = 0; /* irrelevant */
272 entries[0].a_access_mask = (mode >> 6) & 7;
273 entries[1].a_type = OLD_ALLOW;
274 entries[1].a_flags = OLD_ACE_GROUP;
275 entries[1].a_who = 0; /* irrelevant */
276 entries[1].a_access_mask = (mode >> 3) & 7;
277 entries[2].a_type = OLD_ALLOW;
278 entries[2].a_flags = OLD_ACE_OTHER;
279 entries[2].a_who = 0;
280 entries[2].a_access_mask = mode & 7;
285 /* Running on Solaris 10 (newer version) or Solaris 11.
286 The details here were found through "/bin/ls -lvd somefiles". */
287 entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
288 entries[0].a_flags = NEW_ACE_OWNER;
289 entries[0].a_who = 0; /* irrelevant */
290 entries[0].a_access_mask = 0;
291 entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
292 entries[1].a_flags = NEW_ACE_OWNER;
293 entries[1].a_who = 0; /* irrelevant */
294 entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
295 | NEW_ACE_WRITE_ATTRIBUTES
297 | NEW_ACE_WRITE_OWNER;
299 entries[1].a_access_mask |= NEW_ACE_READ_DATA;
301 entries[0].a_access_mask |= NEW_ACE_READ_DATA;
303 entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
305 entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
307 entries[1].a_access_mask |= NEW_ACE_EXECUTE;
309 entries[0].a_access_mask |= NEW_ACE_EXECUTE;
310 entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
311 entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
312 entries[2].a_who = 0; /* irrelevant */
313 entries[2].a_access_mask = 0;
314 entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
315 entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
316 entries[3].a_who = 0; /* irrelevant */
317 entries[3].a_access_mask = 0;
319 entries[3].a_access_mask |= NEW_ACE_READ_DATA;
321 entries[2].a_access_mask |= NEW_ACE_READ_DATA;
323 entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
325 entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
327 entries[3].a_access_mask |= NEW_ACE_EXECUTE;
329 entries[2].a_access_mask |= NEW_ACE_EXECUTE;
330 entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
331 entries[4].a_flags = NEW_ACE_EVERYONE;
332 entries[4].a_who = 0;
333 entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
334 | NEW_ACE_WRITE_ATTRIBUTES
336 | NEW_ACE_WRITE_OWNER;
337 entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
338 entries[5].a_flags = NEW_ACE_EVERYONE;
339 entries[5].a_who = 0;
340 entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
341 | NEW_ACE_READ_ATTRIBUTES
343 | NEW_ACE_SYNCHRONIZE;
345 entries[5].a_access_mask |= NEW_ACE_READ_DATA;
347 entries[4].a_access_mask |= NEW_ACE_READ_DATA;
349 entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
351 entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
353 entries[5].a_access_mask |= NEW_ACE_EXECUTE;
355 entries[4].a_access_mask |= NEW_ACE_EXECUTE;
359 ret = facl (desc, ACE_SETACL, count, entries);
361 ret = acl (name, ACE_SETACL, count, entries);
362 if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
365 return chmod_or_fchmod (name, desc, mode);
378 entries[0].a_type = USER_OBJ;
379 entries[0].a_id = 0; /* irrelevant */
380 entries[0].a_perm = (mode >> 6) & 7;
381 entries[1].a_type = GROUP_OBJ;
382 entries[1].a_id = 0; /* irrelevant */
383 entries[1].a_perm = (mode >> 3) & 7;
384 entries[2].a_type = OTHER_OBJ;
386 entries[2].a_perm = mode & 7;
389 ret = facl (desc, SETACL,
390 sizeof (entries) / sizeof (aclent_t), entries);
392 ret = acl (name, SETACL,
393 sizeof (entries) / sizeof (aclent_t), entries);
396 if (errno == ENOSYS || errno == EOPNOTSUPP)
397 return chmod_or_fchmod (name, desc, mode);
402 if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
404 /* We did not call chmod so far, so the special bits have not yet
406 return chmod_or_fchmod (name, desc, mode);
410 # elif HAVE_GETACL /* HP-UX */
416 ret = fstat (desc, &statbuf);
418 ret = stat (name, &statbuf);
423 struct acl_entry entries[3];
425 entries[0].uid = statbuf.st_uid;
426 entries[0].gid = ACL_NSGROUP;
427 entries[0].mode = (mode >> 6) & 7;
428 entries[1].uid = ACL_NSUSER;
429 entries[1].gid = statbuf.st_gid;
430 entries[1].mode = (mode >> 3) & 7;
431 entries[2].uid = ACL_NSUSER;
432 entries[2].gid = ACL_NSGROUP;
433 entries[2].mode = mode & 7;
436 ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
438 ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
442 if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
445 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
447 struct acl entries[4];
449 entries[0].a_type = USER_OBJ;
450 entries[0].a_id = 0; /* irrelevant */
451 entries[0].a_perm = (mode >> 6) & 7;
452 entries[1].a_type = GROUP_OBJ;
453 entries[1].a_id = 0; /* irrelevant */
454 entries[1].a_perm = (mode >> 3) & 7;
455 entries[2].a_type = CLASS_OBJ;
457 entries[2].a_perm = (mode >> 3) & 7;
458 entries[3].a_type = OTHER_OBJ;
460 entries[3].a_perm = mode & 7;
462 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
468 return chmod_or_fchmod (name, desc, mode);
472 ret = acl ((char *) name, ACL_SET,
473 sizeof (entries) / sizeof (struct acl), entries);
476 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
477 return chmod_or_fchmod (name, desc, mode);
482 return chmod_or_fchmod (name, desc, mode);
486 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
488 /* We did not call chmod so far, so the special bits have not yet
490 return chmod_or_fchmod (name, desc, mode);
494 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
496 acl_type_list_t types;
497 size_t types_size = sizeof (types);
500 if (aclx_gettypes (name, &types, &types_size) < 0
501 || types.num_entries == 0)
502 return chmod_or_fchmod (name, desc, mode);
504 /* XXX Do we need to clear all types of ACLs for the given file, or is it
505 sufficient to clear the first one? */
506 type = types.entries[0];
507 if (type.u64 == ACL_AIXC)
509 union { struct acl a; char room[128]; } u;
512 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
513 u.a.acl_mode = mode & ~(S_IXACL | 0777);
514 u.a.u_access = (mode >> 6) & 7;
515 u.a.g_access = (mode >> 3) & 7;
516 u.a.o_access = mode & 7;
519 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
520 type, &u.a, u.a.acl_len, mode);
522 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
523 type, &u.a, u.a.acl_len, mode);
524 if (!(ret < 0 && errno == ENOSYS))
527 else if (type.u64 == ACL_NFS4)
529 union { nfs4_acl_int_t a; char room[128]; } u;
533 u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
535 ace = &u.a.aclEntry[0];
537 ace->flags = ACE4_ID_SPECIAL;
538 ace->aceWho.special_whoid = ACE4_WHO_OWNER;
539 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
542 (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
544 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
545 | ACE4_ADD_SUBDIRECTORY
547 | (mode & 0100 ? ACE4_EXECUTE : 0);
548 ace->aceWhoString[0] = '\0';
549 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
550 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
554 ace->flags = ACE4_ID_SPECIAL;
555 ace->aceWho.special_whoid = ACE4_WHO_GROUP;
556 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
559 (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
561 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
562 | ACE4_ADD_SUBDIRECTORY
564 | (mode & 0010 ? ACE4_EXECUTE : 0);
565 ace->aceWhoString[0] = '\0';
566 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
567 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
571 ace->flags = ACE4_ID_SPECIAL;
572 ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
573 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
576 (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
578 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
579 | ACE4_ADD_SUBDIRECTORY
581 | (mode & 0001 ? ACE4_EXECUTE : 0);
582 ace->aceWhoString[0] = '\0';
583 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
584 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
587 u.a.aclLength = (char *) ace - (char *) &u.a;
590 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
591 type, &u.a, u.a.aclLength, mode);
593 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
594 type, &u.a, u.a.aclLength, mode);
595 if (!(ret < 0 && errno == ENOSYS))
599 return chmod_or_fchmod (name, desc, mode);
601 # elif HAVE_STATACL /* older AIX */
603 union { struct acl a; char room[128]; } u;
606 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
607 u.a.acl_mode = mode & ~(S_IXACL | 0777);
608 u.a.u_access = (mode >> 6) & 7;
609 u.a.g_access = (mode >> 3) & 7;
610 u.a.o_access = mode & 7;
613 ret = fchacl (desc, &u.a, u.a.acl_len);
615 ret = chacl (name, &u.a, u.a.acl_len);
617 if (ret < 0 && errno == ENOSYS)
618 return chmod_or_fchmod (name, desc, mode);
622 # elif HAVE_ACLSORT /* NonStop Kernel */
624 struct acl entries[4];
627 entries[0].a_type = USER_OBJ;
628 entries[0].a_id = 0; /* irrelevant */
629 entries[0].a_perm = (mode >> 6) & 7;
630 entries[1].a_type = GROUP_OBJ;
631 entries[1].a_id = 0; /* irrelevant */
632 entries[1].a_perm = (mode >> 3) & 7;
633 entries[2].a_type = CLASS_OBJ;
635 entries[2].a_perm = (mode >> 3) & 7;
636 entries[3].a_type = OTHER_OBJ;
638 entries[3].a_perm = mode & 7;
640 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
646 return chmod_or_fchmod (name, desc, mode);
650 ret = acl ((char *) name, ACL_SET,
651 sizeof (entries) / sizeof (struct acl), entries);
655 return chmod_or_fchmod (name, desc, mode);
659 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
661 /* We did not call chmod so far, so the special bits have not yet
663 return chmod_or_fchmod (name, desc, mode);
667 # else /* Unknown flavor of ACLs */
668 return chmod_or_fchmod (name, desc, mode);
671 return chmod_or_fchmod (name, desc, mode);
675 /* As with qset_acl, but also output a diagnostic on failure. */
678 set_acl (char const *name, int desc, mode_t mode)
680 int r = qset_acl (name, desc, mode);
682 error (0, errno, _("setting permissions for %s"), quote (name));