1 /* set-mode-acl.c - set access control list equivalent to a mode
3 Copyright (C) 2002-2003, 2005-2012 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, Mac OS 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 Mac OS 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 GETACL /* 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. */
217 /* Initially, try to read the entries into a stack-allocated buffer.
218 Use malloc if it does not fit. */
221 alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
222 alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
224 ace_t buf[alloc_init];
225 size_t alloc = alloc_init;
226 ace_t *entries = buf;
227 ace_t *malloced = NULL;
233 ? facl (desc, ACE_GETACL, alloc, entries)
234 : acl (name, ACE_GETACL, alloc, entries));
235 if (count < 0 && errno == ENOSPC)
237 /* Increase the size of the buffer. */
239 if (alloc > alloc_max / 2)
244 alloc = 2 * alloc; /* <= alloc_max */
245 entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
263 for (i = 0; i < count; i++)
264 if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
281 /* Running on Solaris 10. */
282 entries[0].a_type = OLD_ALLOW;
283 entries[0].a_flags = OLD_ACE_OWNER;
284 entries[0].a_who = 0; /* irrelevant */
285 entries[0].a_access_mask = (mode >> 6) & 7;
286 entries[1].a_type = OLD_ALLOW;
287 entries[1].a_flags = OLD_ACE_GROUP;
288 entries[1].a_who = 0; /* irrelevant */
289 entries[1].a_access_mask = (mode >> 3) & 7;
290 entries[2].a_type = OLD_ALLOW;
291 entries[2].a_flags = OLD_ACE_OTHER;
292 entries[2].a_who = 0;
293 entries[2].a_access_mask = mode & 7;
298 /* Running on Solaris 10 (newer version) or Solaris 11.
299 The details here were found through "/bin/ls -lvd somefiles". */
300 entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
301 entries[0].a_flags = NEW_ACE_OWNER;
302 entries[0].a_who = 0; /* irrelevant */
303 entries[0].a_access_mask = 0;
304 entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
305 entries[1].a_flags = NEW_ACE_OWNER;
306 entries[1].a_who = 0; /* irrelevant */
307 entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
308 | NEW_ACE_WRITE_ATTRIBUTES
310 | NEW_ACE_WRITE_OWNER;
312 entries[1].a_access_mask |= NEW_ACE_READ_DATA;
314 entries[0].a_access_mask |= NEW_ACE_READ_DATA;
316 entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
318 entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
320 entries[1].a_access_mask |= NEW_ACE_EXECUTE;
322 entries[0].a_access_mask |= NEW_ACE_EXECUTE;
323 entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
324 entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
325 entries[2].a_who = 0; /* irrelevant */
326 entries[2].a_access_mask = 0;
327 entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
328 entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
329 entries[3].a_who = 0; /* irrelevant */
330 entries[3].a_access_mask = 0;
332 entries[3].a_access_mask |= NEW_ACE_READ_DATA;
334 entries[2].a_access_mask |= NEW_ACE_READ_DATA;
336 entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
338 entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
340 entries[3].a_access_mask |= NEW_ACE_EXECUTE;
342 entries[2].a_access_mask |= NEW_ACE_EXECUTE;
343 entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
344 entries[4].a_flags = NEW_ACE_EVERYONE;
345 entries[4].a_who = 0;
346 entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
347 | NEW_ACE_WRITE_ATTRIBUTES
349 | NEW_ACE_WRITE_OWNER;
350 entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
351 entries[5].a_flags = NEW_ACE_EVERYONE;
352 entries[5].a_who = 0;
353 entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
354 | NEW_ACE_READ_ATTRIBUTES
356 | NEW_ACE_SYNCHRONIZE;
358 entries[5].a_access_mask |= NEW_ACE_READ_DATA;
360 entries[4].a_access_mask |= NEW_ACE_READ_DATA;
362 entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
364 entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
366 entries[5].a_access_mask |= NEW_ACE_EXECUTE;
368 entries[4].a_access_mask |= NEW_ACE_EXECUTE;
372 ret = facl (desc, ACE_SETACL, count, entries);
374 ret = acl (name, ACE_SETACL, count, entries);
375 if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
378 return chmod_or_fchmod (name, desc, mode);
391 entries[0].a_type = USER_OBJ;
392 entries[0].a_id = 0; /* irrelevant */
393 entries[0].a_perm = (mode >> 6) & 7;
394 entries[1].a_type = GROUP_OBJ;
395 entries[1].a_id = 0; /* irrelevant */
396 entries[1].a_perm = (mode >> 3) & 7;
397 entries[2].a_type = OTHER_OBJ;
399 entries[2].a_perm = mode & 7;
402 ret = facl (desc, SETACL,
403 sizeof (entries) / sizeof (aclent_t), entries);
405 ret = acl (name, SETACL,
406 sizeof (entries) / sizeof (aclent_t), entries);
409 if (errno == ENOSYS || errno == EOPNOTSUPP)
410 return chmod_or_fchmod (name, desc, mode);
415 if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
417 /* We did not call chmod so far, so the special bits have not yet
419 return chmod_or_fchmod (name, desc, mode);
423 # elif HAVE_GETACL /* HP-UX */
429 ret = fstat (desc, &statbuf);
431 ret = stat (name, &statbuf);
436 struct acl_entry entries[3];
438 entries[0].uid = statbuf.st_uid;
439 entries[0].gid = ACL_NSGROUP;
440 entries[0].mode = (mode >> 6) & 7;
441 entries[1].uid = ACL_NSUSER;
442 entries[1].gid = statbuf.st_gid;
443 entries[1].mode = (mode >> 3) & 7;
444 entries[2].uid = ACL_NSUSER;
445 entries[2].gid = ACL_NSGROUP;
446 entries[2].mode = mode & 7;
449 ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
451 ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
455 if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
458 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
460 struct acl entries[4];
462 entries[0].a_type = USER_OBJ;
463 entries[0].a_id = 0; /* irrelevant */
464 entries[0].a_perm = (mode >> 6) & 7;
465 entries[1].a_type = GROUP_OBJ;
466 entries[1].a_id = 0; /* irrelevant */
467 entries[1].a_perm = (mode >> 3) & 7;
468 entries[2].a_type = CLASS_OBJ;
470 entries[2].a_perm = (mode >> 3) & 7;
471 entries[3].a_type = OTHER_OBJ;
473 entries[3].a_perm = mode & 7;
475 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
481 return chmod_or_fchmod (name, desc, mode);
485 ret = acl ((char *) name, ACL_SET,
486 sizeof (entries) / sizeof (struct acl), entries);
489 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
490 return chmod_or_fchmod (name, desc, mode);
495 return chmod_or_fchmod (name, desc, mode);
499 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
501 /* We did not call chmod so far, so the special bits have not yet
503 return chmod_or_fchmod (name, desc, mode);
507 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
509 acl_type_list_t types;
510 size_t types_size = sizeof (types);
513 if (aclx_gettypes (name, &types, &types_size) < 0
514 || types.num_entries == 0)
515 return chmod_or_fchmod (name, desc, mode);
517 /* XXX Do we need to clear all types of ACLs for the given file, or is it
518 sufficient to clear the first one? */
519 type = types.entries[0];
520 if (type.u64 == ACL_AIXC)
522 union { struct acl a; char room[128]; } u;
525 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
526 u.a.acl_mode = mode & ~(S_IXACL | 0777);
527 u.a.u_access = (mode >> 6) & 7;
528 u.a.g_access = (mode >> 3) & 7;
529 u.a.o_access = mode & 7;
532 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
533 type, &u.a, u.a.acl_len, mode);
535 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
536 type, &u.a, u.a.acl_len, mode);
537 if (!(ret < 0 && errno == ENOSYS))
540 else if (type.u64 == ACL_NFS4)
542 union { nfs4_acl_int_t a; char room[128]; } u;
546 u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
548 ace = &u.a.aclEntry[0];
550 ace->flags = ACE4_ID_SPECIAL;
551 ace->aceWho.special_whoid = ACE4_WHO_OWNER;
552 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
555 (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
557 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
558 | ACE4_ADD_SUBDIRECTORY
560 | (mode & 0100 ? 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];
567 ace->flags = ACE4_ID_SPECIAL;
568 ace->aceWho.special_whoid = ACE4_WHO_GROUP;
569 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
572 (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
574 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
575 | ACE4_ADD_SUBDIRECTORY
577 | (mode & 0010 ? 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];
584 ace->flags = ACE4_ID_SPECIAL;
585 ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
586 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
589 (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
591 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
592 | ACE4_ADD_SUBDIRECTORY
594 | (mode & 0001 ? ACE4_EXECUTE : 0);
595 ace->aceWhoString[0] = '\0';
596 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
597 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
600 u.a.aclLength = (char *) ace - (char *) &u.a;
603 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
604 type, &u.a, u.a.aclLength, mode);
606 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
607 type, &u.a, u.a.aclLength, mode);
608 if (!(ret < 0 && errno == ENOSYS))
612 return chmod_or_fchmod (name, desc, mode);
614 # elif HAVE_STATACL /* older AIX */
616 union { struct acl a; char room[128]; } u;
619 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
620 u.a.acl_mode = mode & ~(S_IXACL | 0777);
621 u.a.u_access = (mode >> 6) & 7;
622 u.a.g_access = (mode >> 3) & 7;
623 u.a.o_access = mode & 7;
626 ret = fchacl (desc, &u.a, u.a.acl_len);
628 ret = chacl (name, &u.a, u.a.acl_len);
630 if (ret < 0 && errno == ENOSYS)
631 return chmod_or_fchmod (name, desc, mode);
635 # elif HAVE_ACLSORT /* NonStop Kernel */
637 struct acl entries[4];
640 entries[0].a_type = USER_OBJ;
641 entries[0].a_id = 0; /* irrelevant */
642 entries[0].a_perm = (mode >> 6) & 7;
643 entries[1].a_type = GROUP_OBJ;
644 entries[1].a_id = 0; /* irrelevant */
645 entries[1].a_perm = (mode >> 3) & 7;
646 entries[2].a_type = CLASS_OBJ;
648 entries[2].a_perm = (mode >> 3) & 7;
649 entries[3].a_type = OTHER_OBJ;
651 entries[3].a_perm = mode & 7;
653 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
659 return chmod_or_fchmod (name, desc, mode);
663 ret = acl ((char *) name, ACL_SET,
664 sizeof (entries) / sizeof (struct acl), entries);
668 return chmod_or_fchmod (name, desc, mode);
672 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
674 /* We did not call chmod so far, so the special bits have not yet
676 return chmod_or_fchmod (name, desc, mode);
680 # else /* Unknown flavor of ACLs */
681 return chmod_or_fchmod (name, desc, mode);
684 return chmod_or_fchmod (name, desc, mode);
688 /* As with qset_acl, but also output a diagnostic on failure. */
691 set_acl (char const *name, int desc, mode_t mode)
693 int ret = qset_acl (name, desc, mode);
695 error (0, errno, _("setting permissions for %s"), quote (name));