1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* qset-acl.c - set access control list equivalent to a mode
5 Copyright (C) 2002-2003, 2005-2013 Free Software Foundation, Inc.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Written by Paul Eggert and Andreas Gruenbacher, and Bruno Haible. */
24 #define ACL_INTERNAL_INLINE _GL_EXTERN_INLINE
28 #include "acl-internal.h"
31 /* If DESC is a valid file descriptor use fchmod to change the
32 file's mode to MODE on systems that have fchmod. On systems
33 that don't have fchmod and if DESC is invalid, use chmod on
35 Return 0 if successful. Return -1 and set errno upon failure. */
38 chmod_or_fchmod (const char *name, int desc, mode_t mode)
40 if (HAVE_FCHMOD && desc != -1)
41 return fchmod (desc, mode);
43 return chmod (name, mode);
46 /* Set the access control lists of a file. If DESC is a valid file
47 descriptor, use file descriptor operations where available, else use
48 filename based operations on NAME. If access control lists are not
49 available, fchmod the target file to MODE. Also sets the
50 non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX)
51 to those from MODE if any are set.
52 Return 0 if successful. Return -1 and set errno upon failure. */
55 qset_acl (char const *name, int desc, mode_t mode)
58 # if HAVE_ACL_GET_FILE
59 /* POSIX 1003.1e draft 17 (abandoned) specific version. */
60 /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
61 # if !HAVE_ACL_TYPE_EXTENDED
62 /* Linux, FreeBSD, IRIX, Tru64 */
64 /* We must also have acl_from_text and acl_delete_def_file.
65 (acl_delete_def_file could be emulated with acl_init followed
66 by acl_set_file, but acl_set_file with an empty acl is
69 # ifndef HAVE_ACL_FROM_TEXT
70 # error Must have acl_from_text (see POSIX 1003.1e draft 17).
72 # ifndef HAVE_ACL_DELETE_DEF_FILE
73 # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
79 if (HAVE_ACL_FROM_MODE) /* Linux */
81 acl = acl_from_mode (mode);
85 else /* FreeBSD, IRIX, Tru64 */
87 /* If we were to create the ACL using the functions acl_init(),
88 acl_create_entry(), acl_set_tag_type(), acl_set_qualifier(),
89 acl_get_permset(), acl_clear_perm[s](), acl_add_perm(), we
90 would need to create a qualifier. I don't know how to do this.
91 So create it using acl_from_text(). */
93 # if HAVE_ACL_FREE_TEXT /* Tru64 */
94 char acl_text[] = "u::---,g::---,o::---,";
95 # else /* FreeBSD, IRIX */
96 char acl_text[] = "u::---,g::---,o::---";
99 if (mode & S_IRUSR) acl_text[ 3] = 'r';
100 if (mode & S_IWUSR) acl_text[ 4] = 'w';
101 if (mode & S_IXUSR) acl_text[ 5] = 'x';
102 if (mode & S_IRGRP) acl_text[10] = 'r';
103 if (mode & S_IWGRP) acl_text[11] = 'w';
104 if (mode & S_IXGRP) acl_text[12] = 'x';
105 if (mode & S_IROTH) acl_text[17] = 'r';
106 if (mode & S_IWOTH) acl_text[18] = 'w';
107 if (mode & S_IXOTH) acl_text[19] = 'x';
109 acl = acl_from_text (acl_text);
113 if (HAVE_ACL_SET_FD && desc != -1)
114 ret = acl_set_fd (desc, acl);
116 ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
119 int saved_errno = errno;
121 if (! acl_errno_valid (errno))
122 return chmod_or_fchmod (name, desc, mode);
129 if (S_ISDIR (mode) && acl_delete_def_file (name))
132 if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
134 /* We did not call chmod so far, and either the mode and the ACL are
135 separate or special bits are to be set which don't fit into ACLs. */
136 return chmod_or_fchmod (name, desc, mode);
140 # else /* HAVE_ACL_TYPE_EXTENDED */
143 /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
144 and acl_get_file (name, ACL_TYPE_DEFAULT)
145 always return NULL / EINVAL. You have to use
146 acl_get_file (name, ACL_TYPE_EXTENDED)
147 or acl_get_fd (open (name, ...))
150 acl_set_file (name, ACL_TYPE_ACCESS, acl)
151 and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
152 have the same effect as
153 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
154 Each of these calls sets the file's ACL. */
159 /* Remove the ACL if the file has ACLs. */
160 if (HAVE_ACL_GET_FD && desc != -1)
161 acl = acl_get_fd (desc);
163 acl = acl_get_file (name, ACL_TYPE_EXTENDED);
171 if (HAVE_ACL_SET_FD && desc != -1)
172 ret = acl_set_fd (desc, acl);
174 ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
177 int saved_errno = errno;
179 if (! acl_errno_valid (saved_errno))
180 return chmod_or_fchmod (name, desc, mode);
188 /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
189 return chmod_or_fchmod (name, desc, mode);
192 # elif HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
197 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
198 file systems (whereas the other ones are used in UFS file systems). */
200 /* The flags in the ace_t structure changed in a binary incompatible way
201 when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
202 How to distinguish the two conventions at runtime?
203 We fetch the existing ACL. In the old convention, usually three ACEs have
204 a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
205 In the new convention, these values are not used. */
209 /* Initially, try to read the entries into a stack-allocated buffer.
210 Use malloc if it does not fit. */
213 alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
214 alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
216 ace_t buf[alloc_init];
217 size_t alloc = alloc_init;
218 ace_t *entries = buf;
219 ace_t *malloced = NULL;
225 ? facl (desc, ACE_GETACL, alloc, entries)
226 : acl (name, ACE_GETACL, alloc, entries));
227 if (count < 0 && errno == ENOSPC)
229 /* Increase the size of the buffer. */
231 if (alloc > alloc_max / 2)
236 alloc = 2 * alloc; /* <= alloc_max */
237 entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
255 for (i = 0; i < count; i++)
256 if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
273 /* Running on Solaris 10. */
274 entries[0].a_type = OLD_ALLOW;
275 entries[0].a_flags = OLD_ACE_OWNER;
276 entries[0].a_who = 0; /* irrelevant */
277 entries[0].a_access_mask = (mode >> 6) & 7;
278 entries[1].a_type = OLD_ALLOW;
279 entries[1].a_flags = OLD_ACE_GROUP;
280 entries[1].a_who = 0; /* irrelevant */
281 entries[1].a_access_mask = (mode >> 3) & 7;
282 entries[2].a_type = OLD_ALLOW;
283 entries[2].a_flags = OLD_ACE_OTHER;
284 entries[2].a_who = 0;
285 entries[2].a_access_mask = mode & 7;
290 /* Running on Solaris 10 (newer version) or Solaris 11.
291 The details here were found through "/bin/ls -lvd somefiles". */
292 entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
293 entries[0].a_flags = NEW_ACE_OWNER;
294 entries[0].a_who = 0; /* irrelevant */
295 entries[0].a_access_mask = 0;
296 entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
297 entries[1].a_flags = NEW_ACE_OWNER;
298 entries[1].a_who = 0; /* irrelevant */
299 entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
300 | NEW_ACE_WRITE_ATTRIBUTES
302 | NEW_ACE_WRITE_OWNER;
304 entries[1].a_access_mask |= NEW_ACE_READ_DATA;
306 entries[0].a_access_mask |= NEW_ACE_READ_DATA;
308 entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
310 entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
312 entries[1].a_access_mask |= NEW_ACE_EXECUTE;
314 entries[0].a_access_mask |= NEW_ACE_EXECUTE;
315 entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
316 entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
317 entries[2].a_who = 0; /* irrelevant */
318 entries[2].a_access_mask = 0;
319 entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
320 entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
321 entries[3].a_who = 0; /* irrelevant */
322 entries[3].a_access_mask = 0;
324 entries[3].a_access_mask |= NEW_ACE_READ_DATA;
326 entries[2].a_access_mask |= NEW_ACE_READ_DATA;
328 entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
330 entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
332 entries[3].a_access_mask |= NEW_ACE_EXECUTE;
334 entries[2].a_access_mask |= NEW_ACE_EXECUTE;
335 entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
336 entries[4].a_flags = NEW_ACE_EVERYONE;
337 entries[4].a_who = 0;
338 entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
339 | NEW_ACE_WRITE_ATTRIBUTES
341 | NEW_ACE_WRITE_OWNER;
342 entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
343 entries[5].a_flags = NEW_ACE_EVERYONE;
344 entries[5].a_who = 0;
345 entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
346 | NEW_ACE_READ_ATTRIBUTES
348 | NEW_ACE_SYNCHRONIZE;
350 entries[5].a_access_mask |= NEW_ACE_READ_DATA;
352 entries[4].a_access_mask |= NEW_ACE_READ_DATA;
354 entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
356 entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
358 entries[5].a_access_mask |= NEW_ACE_EXECUTE;
360 entries[4].a_access_mask |= NEW_ACE_EXECUTE;
364 ret = facl (desc, ACE_SETACL, count, entries);
366 ret = acl (name, ACE_SETACL, count, entries);
367 if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
370 return chmod_or_fchmod (name, desc, mode);
383 entries[0].a_type = USER_OBJ;
384 entries[0].a_id = 0; /* irrelevant */
385 entries[0].a_perm = (mode >> 6) & 7;
386 entries[1].a_type = GROUP_OBJ;
387 entries[1].a_id = 0; /* irrelevant */
388 entries[1].a_perm = (mode >> 3) & 7;
389 entries[2].a_type = OTHER_OBJ;
391 entries[2].a_perm = mode & 7;
394 ret = facl (desc, SETACL,
395 sizeof (entries) / sizeof (aclent_t), entries);
397 ret = acl (name, SETACL,
398 sizeof (entries) / sizeof (aclent_t), entries);
401 if (errno == ENOSYS || errno == EOPNOTSUPP)
402 return chmod_or_fchmod (name, desc, mode);
407 if (!MODE_INSIDE_ACL || (mode & (S_ISUID | S_ISGID | S_ISVTX)))
409 /* We did not call chmod so far, so the special bits have not yet
411 return chmod_or_fchmod (name, desc, mode);
415 # elif HAVE_GETACL /* HP-UX */
421 ret = fstat (desc, &statbuf);
423 ret = stat (name, &statbuf);
428 struct acl_entry entries[3];
430 entries[0].uid = statbuf.st_uid;
431 entries[0].gid = ACL_NSGROUP;
432 entries[0].mode = (mode >> 6) & 7;
433 entries[1].uid = ACL_NSUSER;
434 entries[1].gid = statbuf.st_gid;
435 entries[1].mode = (mode >> 3) & 7;
436 entries[2].uid = ACL_NSUSER;
437 entries[2].gid = ACL_NSGROUP;
438 entries[2].mode = mode & 7;
441 ret = fsetacl (desc, sizeof (entries) / sizeof (struct acl_entry), entries);
443 ret = setacl (name, sizeof (entries) / sizeof (struct acl_entry), entries);
447 if (!(errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP))
450 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
452 struct acl entries[4];
454 entries[0].a_type = USER_OBJ;
455 entries[0].a_id = 0; /* irrelevant */
456 entries[0].a_perm = (mode >> 6) & 7;
457 entries[1].a_type = GROUP_OBJ;
458 entries[1].a_id = 0; /* irrelevant */
459 entries[1].a_perm = (mode >> 3) & 7;
460 entries[2].a_type = CLASS_OBJ;
462 entries[2].a_perm = (mode >> 3) & 7;
463 entries[3].a_type = OTHER_OBJ;
465 entries[3].a_perm = mode & 7;
467 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
473 return chmod_or_fchmod (name, desc, mode);
477 ret = acl ((char *) name, ACL_SET,
478 sizeof (entries) / sizeof (struct acl), entries);
481 if (errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
482 return chmod_or_fchmod (name, desc, mode);
487 return chmod_or_fchmod (name, desc, mode);
491 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
493 /* We did not call chmod so far, so the special bits have not yet
495 return chmod_or_fchmod (name, desc, mode);
499 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
501 acl_type_list_t types;
502 size_t types_size = sizeof (types);
505 if (aclx_gettypes (name, &types, &types_size) < 0
506 || types.num_entries == 0)
507 return chmod_or_fchmod (name, desc, mode);
509 /* XXX Do we need to clear all types of ACLs for the given file, or is it
510 sufficient to clear the first one? */
511 type = types.entries[0];
512 if (type.u64 == ACL_AIXC)
514 union { struct acl a; char room[128]; } u;
517 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
518 u.a.acl_mode = mode & ~(S_IXACL | 0777);
519 u.a.u_access = (mode >> 6) & 7;
520 u.a.g_access = (mode >> 3) & 7;
521 u.a.o_access = mode & 7;
524 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
525 type, &u.a, u.a.acl_len, mode);
527 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
528 type, &u.a, u.a.acl_len, mode);
529 if (!(ret < 0 && errno == ENOSYS))
532 else if (type.u64 == ACL_NFS4)
534 union { nfs4_acl_int_t a; char room[128]; } u;
538 u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
540 ace = &u.a.aclEntry[0];
542 ace->flags = ACE4_ID_SPECIAL;
543 ace->aceWho.special_whoid = ACE4_WHO_OWNER;
544 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
547 (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
549 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
550 | ACE4_ADD_SUBDIRECTORY
552 | (mode & 0100 ? ACE4_EXECUTE : 0);
553 ace->aceWhoString[0] = '\0';
554 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
555 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
559 ace->flags = ACE4_ID_SPECIAL;
560 ace->aceWho.special_whoid = ACE4_WHO_GROUP;
561 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
564 (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
566 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
567 | ACE4_ADD_SUBDIRECTORY
569 | (mode & 0010 ? ACE4_EXECUTE : 0);
570 ace->aceWhoString[0] = '\0';
571 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
572 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
576 ace->flags = ACE4_ID_SPECIAL;
577 ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
578 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
581 (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
583 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
584 | ACE4_ADD_SUBDIRECTORY
586 | (mode & 0001 ? ACE4_EXECUTE : 0);
587 ace->aceWhoString[0] = '\0';
588 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
589 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
592 u.a.aclLength = (char *) ace - (char *) &u.a;
595 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
596 type, &u.a, u.a.aclLength, mode);
598 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
599 type, &u.a, u.a.aclLength, mode);
600 if (!(ret < 0 && errno == ENOSYS))
604 return chmod_or_fchmod (name, desc, mode);
606 # elif HAVE_STATACL /* older AIX */
608 union { struct acl a; char room[128]; } u;
611 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
612 u.a.acl_mode = mode & ~(S_IXACL | 0777);
613 u.a.u_access = (mode >> 6) & 7;
614 u.a.g_access = (mode >> 3) & 7;
615 u.a.o_access = mode & 7;
618 ret = fchacl (desc, &u.a, u.a.acl_len);
620 ret = chacl (name, &u.a, u.a.acl_len);
622 if (ret < 0 && errno == ENOSYS)
623 return chmod_or_fchmod (name, desc, mode);
627 # elif HAVE_ACLSORT /* NonStop Kernel */
629 struct acl entries[4];
632 entries[0].a_type = USER_OBJ;
633 entries[0].a_id = 0; /* irrelevant */
634 entries[0].a_perm = (mode >> 6) & 7;
635 entries[1].a_type = GROUP_OBJ;
636 entries[1].a_id = 0; /* irrelevant */
637 entries[1].a_perm = (mode >> 3) & 7;
638 entries[2].a_type = CLASS_OBJ;
640 entries[2].a_perm = (mode >> 3) & 7;
641 entries[3].a_type = OTHER_OBJ;
643 entries[3].a_perm = mode & 7;
645 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
651 return chmod_or_fchmod (name, desc, mode);
655 ret = acl ((char *) name, ACL_SET,
656 sizeof (entries) / sizeof (struct acl), entries);
660 return chmod_or_fchmod (name, desc, mode);
664 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
666 /* We did not call chmod so far, so the special bits have not yet
668 return chmod_or_fchmod (name, desc, mode);
672 # else /* Unknown flavor of ACLs */
673 return chmod_or_fchmod (name, desc, mode);
676 return chmod_or_fchmod (name, desc, mode);