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 */
209 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
210 file systems (whereas the other ones are used in UFS file systems). */
212 /* The flags in the ace_t structure changed in a binary incompatible way
213 when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
214 How to distinguish the two conventions at runtime?
215 We fetch the existing ACL. In the old convention, usually three ACEs have
216 a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
217 In the new convention, these values are not used. */
227 count = facl (desc, ACE_GETACLCNT, 0, NULL);
229 count = acl (name, ACE_GETACLCNT, 0, NULL);
235 entries = (ace_t *) malloc (count * sizeof (ace_t));
242 ? facl (desc, ACE_GETACL, count, entries)
243 : acl (name, ACE_GETACL, count, entries))
249 for (i = 0; i < count; i++)
250 if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
258 /* Huh? The number of ACL entries changed since the last call.
272 /* Running on Solaris 10. */
273 entries[0].a_type = OLD_ALLOW;
274 entries[0].a_flags = OLD_ACE_OWNER;
275 entries[0].a_who = 0; /* irrelevant */
276 entries[0].a_access_mask = (mode >> 6) & 7;
277 entries[1].a_type = OLD_ALLOW;
278 entries[1].a_flags = OLD_ACE_GROUP;
279 entries[1].a_who = 0; /* irrelevant */
280 entries[1].a_access_mask = (mode >> 3) & 7;
281 entries[2].a_type = OLD_ALLOW;
282 entries[2].a_flags = OLD_ACE_OTHER;
283 entries[2].a_who = 0;
284 entries[2].a_access_mask = mode & 7;
289 /* Running on Solaris 10 (newer version) or Solaris 11.
290 The details here were found through "/bin/ls -lvd somefiles". */
291 entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
292 entries[0].a_flags = NEW_ACE_OWNER;
293 entries[0].a_who = 0; /* irrelevant */
294 entries[0].a_access_mask = 0;
295 entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
296 entries[1].a_flags = NEW_ACE_OWNER;
297 entries[1].a_who = 0; /* irrelevant */
298 entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
299 | NEW_ACE_WRITE_ATTRIBUTES
301 | NEW_ACE_WRITE_OWNER;
303 entries[1].a_access_mask |= NEW_ACE_READ_DATA;
305 entries[0].a_access_mask |= NEW_ACE_READ_DATA;
307 entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
309 entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
311 entries[1].a_access_mask |= NEW_ACE_EXECUTE;
313 entries[0].a_access_mask |= NEW_ACE_EXECUTE;
314 entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
315 entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
316 entries[2].a_who = 0; /* irrelevant */
317 entries[2].a_access_mask = 0;
318 entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
319 entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
320 entries[3].a_who = 0; /* irrelevant */
321 entries[3].a_access_mask = 0;
323 entries[3].a_access_mask |= NEW_ACE_READ_DATA;
325 entries[2].a_access_mask |= NEW_ACE_READ_DATA;
327 entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
329 entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
331 entries[3].a_access_mask |= NEW_ACE_EXECUTE;
333 entries[2].a_access_mask |= NEW_ACE_EXECUTE;
334 entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
335 entries[4].a_flags = ACE_EVERYONE;
336 entries[4].a_who = 0;
337 entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
338 | NEW_ACE_WRITE_ATTRIBUTES
340 | NEW_ACE_WRITE_OWNER;
341 entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
342 entries[5].a_flags = ACE_EVERYONE;
343 entries[5].a_who = 0;
344 entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
345 | NEW_ACE_READ_ATTRIBUTES
347 | NEW_ACE_SYNCHRONIZE;
349 entries[5].a_access_mask |= NEW_ACE_READ_DATA;
351 entries[4].a_access_mask |= NEW_ACE_READ_DATA;
353 entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
355 entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
357 entries[5].a_access_mask |= NEW_ACE_EXECUTE;
359 entries[4].a_access_mask |= NEW_ACE_EXECUTE;
363 ret = facl (desc, ACE_SETACL, count, entries);
365 ret = acl (name, ACE_SETACL, count, entries);
366 if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
369 return chmod_or_fchmod (name, desc, mode);
382 entries[0].a_type = USER_OBJ;
383 entries[0].a_id = 0; /* irrelevant */
384 entries[0].a_perm = (mode >> 6) & 7;
385 entries[1].a_type = GROUP_OBJ;
386 entries[1].a_id = 0; /* irrelevant */
387 entries[1].a_perm = (mode >> 3) & 7;
388 entries[2].a_type = OTHER_OBJ;
390 entries[2].a_perm = mode & 7;
393 ret = facl (desc, SETACL,
394 sizeof (entries) / sizeof (aclent_t), entries);
396 ret = acl (name, SETACL,
397 sizeof (entries) / sizeof (aclent_t), entries);
400 if (errno == ENOSYS || errno == EOPNOTSUPP)
401 return chmod_or_fchmod (name, desc, mode);
406 if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
408 /* We did not call chmod so far, so the special bits have not yet
410 return chmod_or_fchmod (name, desc, mode);
414 # elif HAVE_GETACL /* HP-UX */
420 ret = fstat (desc, &statbuf);
422 ret = stat (name, &statbuf);
427 struct acl_entry entries[3];
429 entries[0].uid = statbuf.st_uid;
430 entries[0].gid = ACL_NSGROUP;
431 entries[0].mode = (mode >> 6) & 7;
432 entries[1].uid = ACL_NSUSER;
433 entries[1].gid = statbuf.st_gid;
434 entries[1].mode = (mode >> 3) & 7;
435 entries[2].uid = ACL_NSUSER;
436 entries[2].gid = ACL_NSGROUP;
437 entries[2].mode = mode & 7;
440 ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
442 ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
446 if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
449 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
451 struct acl entries[4];
453 entries[0].a_type = USER_OBJ;
454 entries[0].a_id = 0; /* irrelevant */
455 entries[0].a_perm = (mode >> 6) & 7;
456 entries[1].a_type = GROUP_OBJ;
457 entries[1].a_id = 0; /* irrelevant */
458 entries[1].a_perm = (mode >> 3) & 7;
459 entries[2].a_type = CLASS_OBJ;
461 entries[2].a_perm = (mode >> 3) & 7;
462 entries[3].a_type = OTHER_OBJ;
464 entries[3].a_perm = mode & 7;
466 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
472 return chmod_or_fchmod (name, desc, mode);
476 ret = acl ((char *) name, ACL_SET,
477 sizeof (entries) / sizeof (struct acl), entries);
480 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
481 return chmod_or_fchmod (name, desc, mode);
486 return chmod_or_fchmod (name, desc, mode);
490 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
492 /* We did not call chmod so far, so the special bits have not yet
494 return chmod_or_fchmod (name, desc, mode);
498 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
500 acl_type_list_t types;
501 size_t types_size = sizeof (types);
504 if (aclx_gettypes (name, &types, &types_size) < 0
505 || types.num_entries == 0)
506 return chmod_or_fchmod (name, desc, mode);
508 /* XXX Do we need to clear all types of ACLs for the given file, or is it
509 sufficient to clear the first one? */
510 type = types.entries[0];
511 if (type.u64 == ACL_AIXC)
513 union { struct acl a; char room[128]; } u;
516 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
517 u.a.acl_mode = mode & ~(S_IXACL | 0777);
518 u.a.u_access = (mode >> 6) & 7;
519 u.a.g_access = (mode >> 3) & 7;
520 u.a.o_access = mode & 7;
523 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
524 type, &u.a, u.a.acl_len, mode);
526 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
527 type, &u.a, u.a.acl_len, mode);
528 if (!(ret < 0 && errno == ENOSYS))
531 else if (type.u64 == ACL_NFS4)
533 union { nfs4_acl_int_t a; char room[128]; } u;
537 u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
539 ace = &u.a.aclEntry[0];
541 ace->flags = ACE4_ID_SPECIAL;
542 ace->aceWho.special_whoid = ACE4_WHO_OWNER;
543 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
546 (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
548 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
549 | ACE4_ADD_SUBDIRECTORY
551 | (mode & 0100 ? ACE4_EXECUTE : 0);
552 ace->aceWhoString[0] = '\0';
553 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
554 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
558 ace->flags = ACE4_ID_SPECIAL;
559 ace->aceWho.special_whoid = ACE4_WHO_GROUP;
560 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
563 (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
565 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
566 | ACE4_ADD_SUBDIRECTORY
568 | (mode & 0010 ? ACE4_EXECUTE : 0);
569 ace->aceWhoString[0] = '\0';
570 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
571 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
575 ace->flags = ACE4_ID_SPECIAL;
576 ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
577 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
580 (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
582 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
583 | ACE4_ADD_SUBDIRECTORY
585 | (mode & 0001 ? ACE4_EXECUTE : 0);
586 ace->aceWhoString[0] = '\0';
587 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
588 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
591 u.a.aclLength = (char *) ace - (char *) &u.a;
594 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
595 type, &u.a, u.a.aclLength, mode);
597 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
598 type, &u.a, u.a.aclLength, mode);
599 if (!(ret < 0 && errno == ENOSYS))
603 return chmod_or_fchmod (name, desc, mode);
605 # elif HAVE_STATACL /* older AIX */
607 union { struct acl a; char room[128]; } u;
610 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
611 u.a.acl_mode = mode & ~(S_IXACL | 0777);
612 u.a.u_access = (mode >> 6) & 7;
613 u.a.g_access = (mode >> 3) & 7;
614 u.a.o_access = mode & 7;
617 ret = fchacl (desc, &u.a, u.a.acl_len);
619 ret = chacl (name, &u.a, u.a.acl_len);
621 if (ret < 0 && errno == ENOSYS)
622 return chmod_or_fchmod (name, desc, mode);
626 # elif HAVE_ACLSORT /* NonStop Kernel */
628 struct acl entries[4];
631 entries[0].a_type = USER_OBJ;
632 entries[0].a_id = 0; /* irrelevant */
633 entries[0].a_perm = (mode >> 6) & 7;
634 entries[1].a_type = GROUP_OBJ;
635 entries[1].a_id = 0; /* irrelevant */
636 entries[1].a_perm = (mode >> 3) & 7;
637 entries[2].a_type = CLASS_OBJ;
639 entries[2].a_perm = (mode >> 3) & 7;
640 entries[3].a_type = OTHER_OBJ;
642 entries[3].a_perm = mode & 7;
644 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
650 return chmod_or_fchmod (name, desc, mode);
654 ret = acl ((char *) name, ACL_SET,
655 sizeof (entries) / sizeof (struct acl), entries);
659 return chmod_or_fchmod (name, desc, mode);
663 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
665 /* We did not call chmod so far, so the special bits have not yet
667 return chmod_or_fchmod (name, desc, mode);
671 # else /* Unknown flavor of ACLs */
672 return chmod_or_fchmod (name, desc, mode);
675 return chmod_or_fchmod (name, desc, mode);
679 /* As with qset_acl, but also output a diagnostic on failure. */
682 set_acl (char const *name, int desc, mode_t mode)
684 int r = qset_acl (name, desc, mode);
686 error (0, errno, _("setting permissions for %s"), quote (name));