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 */
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 & (S_ISUID | S_ISGID | S_ISVTX))
137 /* We did not call chmod so far, so the special bits have not yet
139 return chmod_or_fchmod (name, desc, mode);
143 # else /* !MODE_INSIDE_ACL */
146 # if !HAVE_ACL_TYPE_EXTENDED
147 # error Must have ACL_TYPE_EXTENDED
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, ...))
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. */
166 /* Remove the ACL if the file has ACLs. */
167 if (HAVE_ACL_GET_FD && desc != -1)
168 acl = acl_get_fd (desc);
170 acl = acl_get_file (name, ACL_TYPE_EXTENDED);
178 if (HAVE_ACL_SET_FD && desc != -1)
179 ret = acl_set_fd (desc, acl);
181 ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
184 int saved_errno = errno;
188 if (ACL_NOT_WELL_SUPPORTED (saved_errno))
189 return chmod_or_fchmod (name, desc, mode);
200 /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
201 return chmod_or_fchmod (name, desc, mode);
204 # elif HAVE_FACL && defined GETACLCNT /* Solaris, Cygwin, not HP-UX */
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, ...). */
212 char acl_text[] = "user::---,group::---,mask:---,other:---";
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';
226 if (acl_fromtext (acl_text, &aclp) != 0)
232 ret = (desc < 0 ? acl_set (name, aclp) : facl_set (desc, aclp));
237 if (saved_errno == ENOSYS || saved_errno == EOPNOTSUPP)
238 return chmod_or_fchmod (name, desc, mode);
243 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
245 /* We did not call chmod so far, so the special bits have not yet
247 return chmod_or_fchmod (name, desc, mode);
251 # else /* Solaris, Cygwin, general case */
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). */
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. */
274 count = facl (desc, ACE_GETACLCNT, 0, NULL);
276 count = acl (name, ACE_GETACLCNT, 0, NULL);
282 entries = (ace_t *) malloc (count * sizeof (ace_t));
289 ? facl (desc, ACE_GETACL, count, entries)
290 : acl (name, ACE_GETACL, count, entries))
296 for (i = 0; i < count; i++)
297 if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
305 /* Huh? The number of ACL entries changed since the last call.
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;
336 /* Running on Solaris 10 (newer version) or Solaris 11.
337 The details here were found through "/bin/ls -lvd somefiles". */
341 if ((desc != -1 ? fstat (desc, &statbuf) : stat (name, &statbuf)) >= 0)
342 dir = S_ISDIR (statbuf.st_mode);
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
356 | NEW_ACE_WRITE_OWNER;
358 entries[1].a_access_mask |= NEW_ACE_READ_DATA;
360 entries[0].a_access_mask |= NEW_ACE_READ_DATA;
362 entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
364 entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
366 entries[1].a_access_mask |= NEW_ACE_EXECUTE;
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;
378 entries[3].a_access_mask |= NEW_ACE_READ_DATA;
380 entries[2].a_access_mask |= NEW_ACE_READ_DATA;
382 entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
384 entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
386 entries[3].a_access_mask |= NEW_ACE_EXECUTE;
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
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
402 | NEW_ACE_SYNCHRONIZE;
404 entries[5].a_access_mask |= NEW_ACE_READ_DATA;
406 entries[4].a_access_mask |= NEW_ACE_READ_DATA;
408 entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
410 entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
412 entries[5].a_access_mask |= NEW_ACE_EXECUTE;
414 entries[4].a_access_mask |= NEW_ACE_EXECUTE;
418 ret = facl (desc, ACE_SETACL, count, entries);
420 ret = acl (name, ACE_SETACL, count, entries);
421 if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
424 return chmod_or_fchmod (name, desc, mode);
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;
445 entries[2].a_perm = mode & 7;
448 ret = facl (desc, SETACL,
449 sizeof (entries) / sizeof (aclent_t), entries);
451 ret = acl (name, SETACL,
452 sizeof (entries) / sizeof (aclent_t), entries);
455 if (errno == ENOSYS || errno == EOPNOTSUPP)
456 return chmod_or_fchmod (name, desc, mode);
461 if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
463 /* We did not call chmod so far, so the special bits have not yet
465 return chmod_or_fchmod (name, desc, mode);
471 # elif HAVE_GETACL /* HP-UX */
477 ret = fstat (desc, &statbuf);
479 ret = stat (name, &statbuf);
484 struct acl_entry entries[3];
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;
497 ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
499 ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
503 if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
506 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
508 struct acl entries[4];
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;
518 entries[2].a_perm = (mode >> 3) & 7;
519 entries[3].a_type = OTHER_OBJ;
521 entries[3].a_perm = mode & 7;
523 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
529 return chmod_or_fchmod (name, desc, mode);
533 ret = acl ((char *) name, ACL_SET,
534 sizeof (entries) / sizeof (struct acl), entries);
537 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
538 return chmod_or_fchmod (name, desc, mode);
543 return chmod_or_fchmod (name, desc, mode);
547 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
549 /* We did not call chmod so far, so the special bits have not yet
551 return chmod_or_fchmod (name, desc, mode);
555 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
557 acl_type_list_t types;
558 size_t types_size = sizeof (types);
561 if (aclx_gettypes (name, &types, &types_size) < 0
562 || types.num_entries == 0)
563 return chmod_or_fchmod (name, desc, mode);
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)
570 union { struct acl a; char room[128]; } u;
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;
580 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
581 type, &u.a, u.a.acl_len, mode);
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))
588 else if (type.u64 == ACL_NFS4)
590 union { nfs4_acl_int_t a; char room[128]; } u;
594 u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
596 ace = &u.a.aclEntry[0];
598 ace->flags = ACE4_ID_SPECIAL;
599 ace->aceWho.special_whoid = ACE4_WHO_OWNER;
600 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
603 (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
605 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
606 | ACE4_ADD_SUBDIRECTORY
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];
615 ace->flags = ACE4_ID_SPECIAL;
616 ace->aceWho.special_whoid = ACE4_WHO_GROUP;
617 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
620 (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
622 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
623 | ACE4_ADD_SUBDIRECTORY
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];
632 ace->flags = ACE4_ID_SPECIAL;
633 ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
634 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
637 (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
639 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
640 | ACE4_ADD_SUBDIRECTORY
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];
648 u.a.aclLength = (char *) ace - (char *) &u.a;
651 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
652 type, &u.a, u.a.aclLength, mode);
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))
660 return chmod_or_fchmod (name, desc, mode);
662 # elif HAVE_STATACL /* older AIX */
664 union { struct acl a; char room[128]; } u;
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;
674 ret = fchacl (desc, &u.a, u.a.acl_len);
676 ret = chacl (name, &u.a, u.a.acl_len);
678 if (ret < 0 && errno == ENOSYS)
679 return chmod_or_fchmod (name, desc, mode);
683 # elif HAVE_ACLSORT /* NonStop Kernel */
685 struct acl entries[4];
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;
696 entries[2].a_perm = (mode >> 3) & 7;
697 entries[3].a_type = OTHER_OBJ;
699 entries[3].a_perm = mode & 7;
701 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
707 return chmod_or_fchmod (name, desc, mode);
711 ret = acl ((char *) name, ACL_SET,
712 sizeof (entries) / sizeof (struct acl), entries);
716 return chmod_or_fchmod (name, desc, mode);
720 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
722 /* We did not call chmod so far, so the special bits have not yet
724 return chmod_or_fchmod (name, desc, mode);
728 # else /* Unknown flavor of ACLs */
729 return chmod_or_fchmod (name, desc, mode);
732 return chmod_or_fchmod (name, desc, mode);
736 /* As with qset_acl, but also output a diagnostic on failure. */
739 set_acl (char const *name, int desc, mode_t mode)
741 int r = qset_acl (name, desc, mode);
743 error (0, errno, _("setting permissions for %s"), quote (name));