Avoid error in TLS connections due to incorrect format
[emacs.git] / lib / set-permissions.c
blob3bcfd31530e54a6681b3dc824a8a056b29a668fa
1 /* set-permissions.c - set permissions of a file
3 Copyright (C) 2002-2003, 2005-2015 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, Andreas Grünbacher, and Bruno Haible. */
20 #include <config.h>
22 #include "acl.h"
24 #include "acl-internal.h"
26 #if USE_ACL
27 # if ! defined HAVE_ACL_FROM_MODE && defined HAVE_ACL_FROM_TEXT /* FreeBSD, IRIX, Tru64 */
28 static acl_t
29 acl_from_mode (mode_t mode)
31 # if HAVE_ACL_FREE_TEXT /* Tru64 */
32 char acl_text[] = "u::---,g::---,o::---,";
33 # else /* FreeBSD, IRIX */
34 char acl_text[] = "u::---,g::---,o::---";
35 # endif
37 if (mode & S_IRUSR) acl_text[ 3] = 'r';
38 if (mode & S_IWUSR) acl_text[ 4] = 'w';
39 if (mode & S_IXUSR) acl_text[ 5] = 'x';
40 if (mode & S_IRGRP) acl_text[10] = 'r';
41 if (mode & S_IWGRP) acl_text[11] = 'w';
42 if (mode & S_IXGRP) acl_text[12] = 'x';
43 if (mode & S_IROTH) acl_text[17] = 'r';
44 if (mode & S_IWOTH) acl_text[18] = 'w';
45 if (mode & S_IXOTH) acl_text[19] = 'x';
47 return acl_from_text (acl_text);
49 # endif
51 # if HAVE_FACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
52 static int
53 set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
55 # ifdef ACE_GETACL
56 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
57 file systems (whereas the other ones are used in UFS file systems). */
59 /* The flags in the ace_t structure changed in a binary incompatible way
60 when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
61 How to distinguish the two conventions at runtime?
62 We fetch the existing ACL. In the old convention, usually three ACEs have
63 a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
64 In the new convention, these values are not used. */
65 int convention;
68 /* Initially, try to read the entries into a stack-allocated buffer.
69 Use malloc if it does not fit. */
70 enum
72 alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
73 alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
75 ace_t buf[alloc_init];
76 size_t alloc = alloc_init;
77 ace_t *entries = buf;
78 ace_t *malloced = NULL;
79 int count;
81 for (;;)
83 count = (desc != -1
84 ? facl (desc, ACE_GETACL, alloc, entries)
85 : acl (name, ACE_GETACL, alloc, entries));
86 if (count < 0 && errno == ENOSPC)
88 /* Increase the size of the buffer. */
89 free (malloced);
90 if (alloc > alloc_max / 2)
92 errno = ENOMEM;
93 return -1;
95 alloc = 2 * alloc; /* <= alloc_max */
96 entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
97 if (entries == NULL)
99 errno = ENOMEM;
100 return -1;
102 continue;
104 break;
107 if (count <= 0)
108 convention = -1;
109 else
111 int i;
113 convention = 0;
114 for (i = 0; i < count; i++)
115 if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
117 convention = 1;
118 break;
121 free (malloced);
124 if (convention >= 0)
126 ace_t entries[6];
127 int count;
128 int ret;
130 if (convention)
132 /* Running on Solaris 10. */
133 entries[0].a_type = OLD_ALLOW;
134 entries[0].a_flags = OLD_ACE_OWNER;
135 entries[0].a_who = 0; /* irrelevant */
136 entries[0].a_access_mask = (mode >> 6) & 7;
137 entries[1].a_type = OLD_ALLOW;
138 entries[1].a_flags = OLD_ACE_GROUP;
139 entries[1].a_who = 0; /* irrelevant */
140 entries[1].a_access_mask = (mode >> 3) & 7;
141 entries[2].a_type = OLD_ALLOW;
142 entries[2].a_flags = OLD_ACE_OTHER;
143 entries[2].a_who = 0;
144 entries[2].a_access_mask = mode & 7;
145 count = 3;
147 else
149 /* Running on Solaris 10 (newer version) or Solaris 11.
150 The details here were found through "/bin/ls -lvd somefiles". */
151 entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
152 entries[0].a_flags = NEW_ACE_OWNER;
153 entries[0].a_who = 0; /* irrelevant */
154 entries[0].a_access_mask = 0;
155 entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
156 entries[1].a_flags = NEW_ACE_OWNER;
157 entries[1].a_who = 0; /* irrelevant */
158 entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
159 | NEW_ACE_WRITE_ATTRIBUTES
160 | NEW_ACE_WRITE_ACL
161 | NEW_ACE_WRITE_OWNER;
162 if (mode & 0400)
163 entries[1].a_access_mask |= NEW_ACE_READ_DATA;
164 else
165 entries[0].a_access_mask |= NEW_ACE_READ_DATA;
166 if (mode & 0200)
167 entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
168 else
169 entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
170 if (mode & 0100)
171 entries[1].a_access_mask |= NEW_ACE_EXECUTE;
172 else
173 entries[0].a_access_mask |= NEW_ACE_EXECUTE;
174 entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
175 entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
176 entries[2].a_who = 0; /* irrelevant */
177 entries[2].a_access_mask = 0;
178 entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
179 entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
180 entries[3].a_who = 0; /* irrelevant */
181 entries[3].a_access_mask = 0;
182 if (mode & 0040)
183 entries[3].a_access_mask |= NEW_ACE_READ_DATA;
184 else
185 entries[2].a_access_mask |= NEW_ACE_READ_DATA;
186 if (mode & 0020)
187 entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
188 else
189 entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
190 if (mode & 0010)
191 entries[3].a_access_mask |= NEW_ACE_EXECUTE;
192 else
193 entries[2].a_access_mask |= NEW_ACE_EXECUTE;
194 entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
195 entries[4].a_flags = NEW_ACE_EVERYONE;
196 entries[4].a_who = 0;
197 entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
198 | NEW_ACE_WRITE_ATTRIBUTES
199 | NEW_ACE_WRITE_ACL
200 | NEW_ACE_WRITE_OWNER;
201 entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
202 entries[5].a_flags = NEW_ACE_EVERYONE;
203 entries[5].a_who = 0;
204 entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
205 | NEW_ACE_READ_ATTRIBUTES
206 | NEW_ACE_READ_ACL
207 | NEW_ACE_SYNCHRONIZE;
208 if (mode & 0004)
209 entries[5].a_access_mask |= NEW_ACE_READ_DATA;
210 else
211 entries[4].a_access_mask |= NEW_ACE_READ_DATA;
212 if (mode & 0002)
213 entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
214 else
215 entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
216 if (mode & 0001)
217 entries[5].a_access_mask |= NEW_ACE_EXECUTE;
218 else
219 entries[4].a_access_mask |= NEW_ACE_EXECUTE;
220 count = 6;
222 if (desc != -1)
223 ret = facl (desc, ACE_SETACL, count, entries);
224 else
225 ret = acl (name, ACE_SETACL, count, entries);
226 if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
228 if (errno == ENOSYS)
230 *must_chmod = true;
231 return 0;
233 return -1;
235 if (ret == 0)
236 return 0;
238 # endif
241 aclent_t entries[3];
242 int ret;
244 entries[0].a_type = USER_OBJ;
245 entries[0].a_id = 0; /* irrelevant */
246 entries[0].a_perm = (mode >> 6) & 7;
247 entries[1].a_type = GROUP_OBJ;
248 entries[1].a_id = 0; /* irrelevant */
249 entries[1].a_perm = (mode >> 3) & 7;
250 entries[2].a_type = OTHER_OBJ;
251 entries[2].a_id = 0;
252 entries[2].a_perm = mode & 7;
254 if (desc != -1)
255 ret = facl (desc, SETACL,
256 sizeof (entries) / sizeof (aclent_t), entries);
257 else
258 ret = acl (name, SETACL,
259 sizeof (entries) / sizeof (aclent_t), entries);
260 if (ret < 0)
262 if (errno == ENOSYS || errno == EOPNOTSUPP)
264 *must_chmod = true;
265 return 0;
267 return -1;
272 #elif HAVE_GETACL /* HP-UX */
273 static int
274 context_acl_from_mode (struct permission_context *ctx, const char *name, int desc)
276 struct stat statbuf;
277 int ret;
279 if (desc != -1)
280 ret = fstat (desc, &statbuf);
281 else
282 ret = stat (name, &statbuf);
283 if (ret < 0)
284 return -1;
286 ctx->entries[0].uid = statbuf.st_uid;
287 ctx->entries[0].gid = ACL_NSGROUP;
288 ctx->entries[0].mode = (mode >> 6) & 7;
289 ctx->entries[1].uid = ACL_NSUSER;
290 ctx->entries[1].gid = statbuf.st_gid;
291 ctx->entries[1].mode = (mode >> 3) & 7;
292 ctx->entries[2].uid = ACL_NSUSER;
293 ctx->entries[2].gid = ACL_NSGROUP;
294 ctx->entries[2].mode = mode & 7;
295 ctx->count = 3;
296 return 0;
299 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
300 static int
301 context_aclv_from_mode (struct permission_context *ctx)
303 int ret;
305 ctx->aclv_entries[0].a_type = USER_OBJ;
306 ctx->aclv_entries[0].a_id = 0; /* irrelevant */
307 ctx->aclv_entries[0].a_perm = (mode >> 6) & 7;
308 ctx->aclv_entries[1].a_type = GROUP_OBJ;
309 ctx->aclv_entries[1].a_id = 0; /* irrelevant */
310 ctx->aclv_entries[1].a_perm = (mode >> 3) & 7;
311 ctx->aclv_entries[2].a_type = CLASS_OBJ;
312 ctx->aclv_entries[2].a_id = 0;
313 ctx->aclv_entries[2].a_perm = (mode >> 3) & 7;
314 ctx->aclv_entries[3].a_type = OTHER_OBJ;
315 ctx->aclv_entries[3].a_id = 0;
316 ctx->aclv_entries[3].a_perm = mode & 7;
317 ctx->aclv_count = 4;
319 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
320 if (ret > 0)
321 abort ();
322 return ret;
324 #endif
326 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
327 static int
328 set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
330 acl_type_list_t types;
331 size_t types_size = sizeof (types);
332 acl_type_t type;
334 if (aclx_gettypes (name, &types, &types_size) < 0
335 || types.num_entries == 0)
337 *must_chmod = true;
338 return 0;
341 /* XXX Do we need to clear all types of ACLs for the given file, or is it
342 sufficient to clear the first one? */
343 type = types.entries[0];
344 if (type.u64 == ACL_AIXC)
346 union { struct acl a; char room[128]; } u;
347 int ret;
349 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
350 u.a.acl_mode = mode & ~(S_IXACL | 0777);
351 u.a.u_access = (mode >> 6) & 7;
352 u.a.g_access = (mode >> 3) & 7;
353 u.a.o_access = mode & 7;
355 if (desc != -1)
356 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
357 type, &u.a, u.a.acl_len, mode);
358 else
359 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
360 type, &u.a, u.a.acl_len, mode);
361 if (!(ret < 0 && errno == ENOSYS))
362 return ret;
364 else if (type.u64 == ACL_NFS4)
366 union { nfs4_acl_int_t a; char room[128]; } u;
367 nfs4_ace_int_t *ace;
368 int ret;
370 u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
371 u.a.aclEntryN = 0;
372 ace = &u.a.aclEntry[0];
374 ace->flags = ACE4_ID_SPECIAL;
375 ace->aceWho.special_whoid = ACE4_WHO_OWNER;
376 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
377 ace->aceFlags = 0;
378 ace->aceMask =
379 (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
380 | (mode & 0200
381 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
382 | ACE4_ADD_SUBDIRECTORY
383 : 0)
384 | (mode & 0100 ? ACE4_EXECUTE : 0);
385 ace->aceWhoString[0] = '\0';
386 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
387 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
388 u.a.aclEntryN++;
391 ace->flags = ACE4_ID_SPECIAL;
392 ace->aceWho.special_whoid = ACE4_WHO_GROUP;
393 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
394 ace->aceFlags = 0;
395 ace->aceMask =
396 (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
397 | (mode & 0020
398 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
399 | ACE4_ADD_SUBDIRECTORY
400 : 0)
401 | (mode & 0010 ? ACE4_EXECUTE : 0);
402 ace->aceWhoString[0] = '\0';
403 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
404 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
405 u.a.aclEntryN++;
408 ace->flags = ACE4_ID_SPECIAL;
409 ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
410 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
411 ace->aceFlags = 0;
412 ace->aceMask =
413 (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
414 | (mode & 0002
415 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
416 | ACE4_ADD_SUBDIRECTORY
417 : 0)
418 | (mode & 0001 ? ACE4_EXECUTE : 0);
419 ace->aceWhoString[0] = '\0';
420 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
421 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
422 u.a.aclEntryN++;
424 u.a.aclLength = (char *) ace - (char *) &u.a;
426 if (desc != -1)
427 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
428 type, &u.a, u.a.aclLength, mode);
429 else
430 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
431 type, &u.a, u.a.aclLength, mode);
432 if (!(ret < 0 && errno == ENOSYS))
433 return ret;
436 *must_chmod = true;
437 return 0;
440 # elif HAVE_STATACL /* older AIX */
441 static int
442 context_acl_from_mode (struct permission_context *ctx)
444 ctx->u.a.acl_len = (char *) &ctx->u.a.acl_ext[0] - (char *) &ctx->u.a; /* no entries */
445 ctx->u.a.acl_mode = ctx->mode & ~(S_IXACL | 0777);
446 ctx->u.a.u_access = (ctx->mode >> 6) & 7;
447 ctx->u.a.g_access = (ctx->mode >> 3) & 7;
448 ctx->u.a.o_access = ctx->mode & 7;
449 ctx->have_u = true;
450 return 0;
453 # elif HAVE_ACLSORT /* NonStop Kernel */
454 static int
455 context_acl_from_mode (struct permission_context *ctx)
457 int ret;
459 ctx->entries[0].a_type = USER_OBJ;
460 ctx->entries[0].a_id = 0; /* irrelevant */
461 ctx->entries[0].a_perm = (mode >> 6) & 7;
462 ctx->entries[1].a_type = GROUP_OBJ;
463 ctx->entries[1].a_id = 0; /* irrelevant */
464 ctx->entries[1].a_perm = (mode >> 3) & 7;
465 ctx->entries[2].a_type = CLASS_OBJ;
466 ctx->entries[2].a_id = 0;
467 ctx->entries[2].a_perm = (mode >> 3) & 7;
468 ctx->entries[3].a_type = OTHER_OBJ;
469 ctx->entries[3].a_id = 0;
470 ctx->entries[3].a_perm = mode & 7;
471 ctx->count = 4;
473 ret = aclsort (sizeof (entries) / sizeof (struct acl), 1, entries);
474 if (ret > 0)
475 abort ();
476 return ret;
478 # endif
480 static int
481 set_acls (struct permission_context *ctx, const char *name, int desc,
482 int from_mode, bool *must_chmod, bool *acls_set)
484 int ret = 0;
486 #if HAVE_ACL_GET_FILE
487 /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
488 /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
489 # if !HAVE_ACL_TYPE_EXTENDED
490 /* Linux, FreeBSD, IRIX, Tru64 */
492 # ifndef HAVE_ACL_FROM_TEXT
493 # error Must have acl_from_text (see POSIX 1003.1e draft 17).
494 # endif
495 # ifndef HAVE_ACL_DELETE_DEF_FILE
496 # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
497 # endif
499 if (! ctx->acls_not_supported)
501 if (ret == 0 && from_mode)
503 if (ctx->acl)
504 acl_free (ctx->acl);
505 ctx->acl = acl_from_mode (ctx->mode);
506 if (ctx->acl == NULL)
507 ret = -1;
510 if (ret == 0 && ctx->acl)
512 if (HAVE_ACL_SET_FD && desc != -1)
513 ret = acl_set_fd (desc, ctx->acl);
514 else
515 ret = acl_set_file (name, ACL_TYPE_ACCESS, ctx->acl);
516 if (ret != 0)
518 if (! acl_errno_valid (errno))
520 ctx->acls_not_supported = true;
521 if (from_mode || acl_access_nontrivial (ctx->acl) == 0)
522 ret = 0;
525 else
527 *acls_set = true;
528 if (S_ISDIR(ctx->mode))
530 if (! from_mode && ctx->default_acl)
531 ret = acl_set_file (name, ACL_TYPE_DEFAULT,
532 ctx->default_acl);
533 else
534 ret = acl_delete_def_file (name);
540 # else /* HAVE_ACL_TYPE_EXTENDED */
541 /* Mac OS X */
543 /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
544 and acl_get_file (name, ACL_TYPE_DEFAULT)
545 always return NULL / EINVAL. You have to use
546 acl_get_file (name, ACL_TYPE_EXTENDED)
547 or acl_get_fd (open (name, ...))
548 to retrieve an ACL.
549 On the other hand,
550 acl_set_file (name, ACL_TYPE_ACCESS, acl)
551 and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
552 have the same effect as
553 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
554 Each of these calls sets the file's ACL. */
556 if (ctx->acl == NULL)
558 acl_t acl;
560 /* Remove ACLs if the file has ACLs. */
561 if (HAVE_ACL_GET_FD && desc != -1)
562 acl = acl_get_fd (desc);
563 else
564 acl = acl_get_file (name, ACL_TYPE_EXTENDED);
565 if (acl)
567 acl_free (acl);
569 acl = acl_init (0);
570 if (acl)
572 if (HAVE_ACL_SET_FD && desc != -1)
573 ret = acl_set_fd (desc, acl);
574 else
575 ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
576 acl_free (acl);
578 else
579 ret = -1;
582 else
584 if (HAVE_ACL_SET_FD && desc != -1)
585 ret = acl_set_fd (desc, ctx->acl);
586 else
587 ret = acl_set_file (name, ACL_TYPE_EXTENDED, ctx->acl);
588 if (ret != 0)
590 if (! acl_errno_valid (errno)
591 && ! acl_extended_nontrivial (ctx->acl))
592 ret = 0;
595 *acls_set = true;
597 # endif
599 # elif defined GETACL /* Solaris, Cygwin, not HP-UX */
601 /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
602 of Unixware. The acl() call returns the access and default ACL both
603 at once. */
605 /* If both ace_entries and entries are available, try SETACL before
606 ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
607 can. */
609 if (from_mode)
610 return set_acls_from_mode (name, desc, ctx->mode, must_chmod);
612 if (ret == 0 && ctx->count)
614 if (desc != -1)
615 ret = facl (desc, SETACL, ctx->count, ctx->entries);
616 else
617 ret = acl (name, SETACL, ctx->count, ctx->entries);
618 if (ret < 0)
620 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
621 && acl_nontrivial (ctx->count, ctx->entries) == 0)
622 ret = 0;
624 else
625 *acls_set = true;
628 # ifdef ACE_GETACL
629 if (ret == 0 && ctx->ace_count)
631 if (desc != -1)
632 ret = facl (desc, ACE_SETACL, ctx->ace_count, ctx->ace_entries);
633 else
634 ret = acl (name, ACE_SETACL, ctx->ace_count, ctx->ace_entries);
635 if (ret < 0)
637 if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
638 && acl_ace_nontrivial (ctx->ace_count, ctx->ace_entries) == 0)
639 ret = 0;
641 else
642 *acls_set = true;
644 # endif
646 #elif HAVE_GETACL /* HP-UX */
648 if (from_mode)
649 ret = context_acl_from_mode (ctx, name, desc);
651 if (ret == 0 && ctx->count > 0)
653 if (desc != -1)
654 ret = fsetacl (desc, ctx->count, ctx->entries);
655 else
656 ret = setacl (name, ctx->count, ctx->entries);
657 if (ret < 0)
659 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
660 && (from_mode || !acl_nontrivial (ctx->count, ctx->entries, &source_statbuf)))
661 ret = 0;
663 else
664 *acls_set = true;
667 # if HAVE_ACLV_H
668 if (from_mode)
669 ret = context_aclv_from_mode (ctx);
671 if (ret == 0 && ctx->aclv_count > 0)
673 ret = acl ((char *) name, ACL_SET, ctx->aclv_count, ctx->aclv_entries);
674 if (ret < 0)
676 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
677 && (from_mode || !aclv_nontrivial (ctx->aclv_count, ctx->aclv_entries)))
678 ret = 0;
680 else
681 *acls_set = true;
683 # endif
685 # elif HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */
687 /* TODO: Implement setting ACLs once get_permissions() reads them. */
689 if (from_mode)
690 ret = set_acls_from_mode (name, desc, mode, must_chmod);
692 # elif HAVE_STATACL /* older AIX */
694 if (from_mode)
695 ret = context_acl_from_mode (ctx);
697 if (ret == 0 && ctx->have_u)
699 if (desc != -1)
700 ret = fchacl (desc, &ctx->u.a, ctx->u.a.acl_len);
701 else
702 ret = chacl ((char *) name, &ctx->u.a, ctx->u.a.acl_len);
703 if (ret < 0)
705 if (errno == ENOSYS && from_mode)
706 ret = 0;
708 else
709 *acls_set = true;
712 # elif HAVE_ACLSORT /* NonStop Kernel */
714 if (from_mode)
715 ret = context_acl_from_mode (ctx);
717 if (ret == 0 && ctx->count)
719 ret = acl ((char *) name, ACL_SET, ctx->count, ctx->entries);
720 if (ret != 0)
722 if (!acl_nontrivial (ctx->count, ctx->entries))
723 ret = 0;
725 else
726 *acls_set = true;
729 # else /* No ACLs */
731 /* Nothing to do. */
733 #endif
735 return ret;
737 #endif
739 /* If DESC is a valid file descriptor use fchmod to change the
740 file's mode to MODE on systems that have fchmod. On systems
741 that don't have fchmod and if DESC is invalid, use chmod on
742 NAME instead.
743 Return 0 if successful. Return -1 and set errno upon failure. */
746 chmod_or_fchmod (const char *name, int desc, mode_t mode)
748 if (HAVE_FCHMOD && desc != -1)
749 return fchmod (desc, mode);
750 else
751 return chmod (name, mode);
754 /* Set the permissions in CTX on a file. If DESC is a valid file descriptor,
755 use file descriptor operations, else use filename based operations on NAME.
756 If access control lists are not available, fchmod the target file to the
757 mode in CTX. Also sets the non-permission bits of the destination file
758 (S_ISUID, S_ISGID, S_ISVTX) to those from the mode in CTX if any are set.
759 Return 0 if successful. Return -1 and set errno upon failure. */
762 set_permissions (struct permission_context *ctx, const char *name, int desc)
764 bool acls_set _GL_UNUSED = false;
765 bool early_chmod;
766 bool must_chmod = false;
767 int ret = 0;
769 #if USE_ACL
770 # if HAVE_STATACL
771 /* older AIX */
772 /* There is no need to call chmod_or_fchmod, since the mode
773 bits S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */
775 early_chmod = false;
776 # else
777 /* All other platforms */
778 /* On Cygwin, it is necessary to call chmod before acl, because
779 chmod can change the contents of the ACL (in ways that don't
780 change the allowed accesses, but still visible). */
782 early_chmod = (! MODE_INSIDE_ACL || (ctx->mode & (S_ISUID | S_ISGID | S_ISVTX)));
783 # endif
784 #else
785 /* No ACLs */
787 early_chmod = true;
788 #endif
790 if (early_chmod)
792 ret = chmod_or_fchmod (name, desc, ctx->mode);
793 if (ret != 0)
794 return -1;
797 #if USE_ACL
798 ret = set_acls (ctx, name, desc, false, &must_chmod, &acls_set);
799 if (! acls_set)
801 int saved_errno = ret ? errno : 0;
803 /* If we can't set an acl which we expect to be able to set, try setting
804 the permissions to ctx->mode. Doe to possible inherited permissions,
805 we cannot simply chmod. */
807 acls_set = false;
808 ret = set_acls (ctx, name, desc, true, &must_chmod, &acls_set);
809 if (! acls_set)
810 must_chmod = true;
812 if (saved_errno)
814 errno = saved_errno;
815 ret = -1;
818 #endif
820 if (must_chmod && ! early_chmod)
822 int saved_errno = ret ? errno : 0;
824 ret = chmod_or_fchmod (name, desc, ctx->mode);
826 if (saved_errno)
828 errno = saved_errno;
829 ret = -1;
833 return ret;