exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / set-permissions.c
blob83a355faa5ce4aefbce312240a4a6ef5048afe99
1 /* Set permissions of a file. -*- coding: utf-8 -*-
3 Copyright (C) 2002-2003, 2005-2024 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 <https://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"
25 #include "minmax.h"
27 #if USE_ACL
28 # if ! defined HAVE_ACL_FROM_MODE && defined HAVE_ACL_FROM_TEXT /* FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
29 # if HAVE_ACL_GET_FILE && !HAVE_ACL_TYPE_EXTENDED
31 static acl_t
32 acl_from_mode (mode_t mode)
34 # if HAVE_ACL_FREE_TEXT /* Tru64 */
35 char acl_text[] = "u::---,g::---,o::---,";
36 # else /* FreeBSD, IRIX, Cygwin >= 2.5 */
37 char acl_text[] = "u::---,g::---,o::---";
38 # endif
40 if (mode & S_IRUSR) acl_text[ 3] = 'r';
41 if (mode & S_IWUSR) acl_text[ 4] = 'w';
42 if (mode & S_IXUSR) acl_text[ 5] = 'x';
43 if (mode & S_IRGRP) acl_text[10] = 'r';
44 if (mode & S_IWGRP) acl_text[11] = 'w';
45 if (mode & S_IXGRP) acl_text[12] = 'x';
46 if (mode & S_IROTH) acl_text[17] = 'r';
47 if (mode & S_IWOTH) acl_text[18] = 'w';
48 if (mode & S_IXOTH) acl_text[19] = 'x';
50 return acl_from_text (acl_text);
52 # endif
53 # endif
55 # if HAVE_FACL && defined GETACL /* Solaris, Cygwin < 2.5, not HP-UX */
56 static int
57 set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
59 # ifdef ACE_GETACL
60 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
61 file systems (whereas the other ones are used in UFS file systems). */
63 /* The flags in the ace_t structure changed in a binary incompatible way
64 when ACL_NO_TRIVIAL etc. were introduced in <sys/acl.h> version 1.15.
65 How to distinguish the two conventions at runtime?
66 We fetch the existing ACL. In the old convention, usually three ACEs have
67 a_flags = ACE_OWNER / ACE_GROUP / ACE_OTHER, in the range 0x0100..0x0400.
68 In the new convention, these values are not used. */
69 int convention;
72 /* Initially, try to read the entries into a stack-allocated buffer.
73 Use malloc if it does not fit. */
74 enum
76 alloc_init = 4000 / sizeof (ace_t), /* >= 3 */
77 alloc_max = MIN (INT_MAX, SIZE_MAX / sizeof (ace_t))
79 ace_t buf[alloc_init];
80 size_t alloc = alloc_init;
81 ace_t *entries = buf;
82 ace_t *malloced = NULL;
83 int count;
85 for (;;)
87 count = (desc != -1
88 ? facl (desc, ACE_GETACL, alloc, entries)
89 : acl (name, ACE_GETACL, alloc, entries));
90 if (count < 0 && errno == ENOSPC)
92 /* Increase the size of the buffer. */
93 free (malloced);
94 if (alloc > alloc_max / 2)
96 errno = ENOMEM;
97 return -1;
99 alloc = 2 * alloc; /* <= alloc_max */
100 entries = malloced = (ace_t *) malloc (alloc * sizeof (ace_t));
101 if (entries == NULL)
103 errno = ENOMEM;
104 return -1;
106 continue;
108 break;
111 if (count <= 0)
112 convention = -1;
113 else
115 int i;
117 convention = 0;
118 for (i = 0; i < count; i++)
119 if (entries[i].a_flags & (OLD_ACE_OWNER | OLD_ACE_GROUP | OLD_ACE_OTHER))
121 convention = 1;
122 break;
125 free (malloced);
128 if (convention >= 0)
130 ace_t entries[6];
131 int count;
132 int ret;
134 if (convention)
136 /* Running on Solaris 10. */
137 entries[0].a_type = OLD_ALLOW;
138 entries[0].a_flags = OLD_ACE_OWNER;
139 entries[0].a_who = 0; /* irrelevant */
140 entries[0].a_access_mask = (mode >> 6) & 7;
141 entries[1].a_type = OLD_ALLOW;
142 entries[1].a_flags = OLD_ACE_GROUP;
143 entries[1].a_who = 0; /* irrelevant */
144 entries[1].a_access_mask = (mode >> 3) & 7;
145 entries[2].a_type = OLD_ALLOW;
146 entries[2].a_flags = OLD_ACE_OTHER;
147 entries[2].a_who = 0;
148 entries[2].a_access_mask = mode & 7;
149 count = 3;
151 else
153 /* Running on Solaris 10 (newer version) or Solaris 11.
154 The details here were found through "/bin/ls -lvd somefiles". */
155 entries[0].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
156 entries[0].a_flags = NEW_ACE_OWNER;
157 entries[0].a_who = 0; /* irrelevant */
158 entries[0].a_access_mask = 0;
159 entries[1].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
160 entries[1].a_flags = NEW_ACE_OWNER;
161 entries[1].a_who = 0; /* irrelevant */
162 entries[1].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
163 | NEW_ACE_WRITE_ATTRIBUTES
164 | NEW_ACE_WRITE_ACL
165 | NEW_ACE_WRITE_OWNER;
166 if (mode & 0400)
167 entries[1].a_access_mask |= NEW_ACE_READ_DATA;
168 else
169 entries[0].a_access_mask |= NEW_ACE_READ_DATA;
170 if (mode & 0200)
171 entries[1].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
172 else
173 entries[0].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
174 if (mode & 0100)
175 entries[1].a_access_mask |= NEW_ACE_EXECUTE;
176 else
177 entries[0].a_access_mask |= NEW_ACE_EXECUTE;
178 entries[2].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
179 entries[2].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
180 entries[2].a_who = 0; /* irrelevant */
181 entries[2].a_access_mask = 0;
182 entries[3].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
183 entries[3].a_flags = NEW_ACE_GROUP | NEW_ACE_IDENTIFIER_GROUP;
184 entries[3].a_who = 0; /* irrelevant */
185 entries[3].a_access_mask = 0;
186 if (mode & 0040)
187 entries[3].a_access_mask |= NEW_ACE_READ_DATA;
188 else
189 entries[2].a_access_mask |= NEW_ACE_READ_DATA;
190 if (mode & 0020)
191 entries[3].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
192 else
193 entries[2].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
194 if (mode & 0010)
195 entries[3].a_access_mask |= NEW_ACE_EXECUTE;
196 else
197 entries[2].a_access_mask |= NEW_ACE_EXECUTE;
198 entries[4].a_type = NEW_ACE_ACCESS_DENIED_ACE_TYPE;
199 entries[4].a_flags = NEW_ACE_EVERYONE;
200 entries[4].a_who = 0;
201 entries[4].a_access_mask = NEW_ACE_WRITE_NAMED_ATTRS
202 | NEW_ACE_WRITE_ATTRIBUTES
203 | NEW_ACE_WRITE_ACL
204 | NEW_ACE_WRITE_OWNER;
205 entries[5].a_type = NEW_ACE_ACCESS_ALLOWED_ACE_TYPE;
206 entries[5].a_flags = NEW_ACE_EVERYONE;
207 entries[5].a_who = 0;
208 entries[5].a_access_mask = NEW_ACE_READ_NAMED_ATTRS
209 | NEW_ACE_READ_ATTRIBUTES
210 | NEW_ACE_READ_ACL
211 | NEW_ACE_SYNCHRONIZE;
212 if (mode & 0004)
213 entries[5].a_access_mask |= NEW_ACE_READ_DATA;
214 else
215 entries[4].a_access_mask |= NEW_ACE_READ_DATA;
216 if (mode & 0002)
217 entries[5].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
218 else
219 entries[4].a_access_mask |= NEW_ACE_WRITE_DATA | NEW_ACE_APPEND_DATA;
220 if (mode & 0001)
221 entries[5].a_access_mask |= NEW_ACE_EXECUTE;
222 else
223 entries[4].a_access_mask |= NEW_ACE_EXECUTE;
224 count = 6;
226 if (desc != -1)
227 ret = facl (desc, ACE_SETACL, count, entries);
228 else
229 ret = acl (name, ACE_SETACL, count, entries);
230 if (ret < 0 && errno != EINVAL && errno != ENOTSUP)
232 if (errno == ENOSYS)
234 *must_chmod = true;
235 return 0;
237 return -1;
239 if (ret == 0)
240 return 0;
242 # endif
245 aclent_t entries[3];
246 int ret;
248 entries[0].a_type = USER_OBJ;
249 entries[0].a_id = 0; /* irrelevant */
250 entries[0].a_perm = (mode >> 6) & 7;
251 entries[1].a_type = GROUP_OBJ;
252 entries[1].a_id = 0; /* irrelevant */
253 entries[1].a_perm = (mode >> 3) & 7;
254 entries[2].a_type = OTHER_OBJ;
255 entries[2].a_id = 0;
256 entries[2].a_perm = mode & 7;
258 if (desc != -1)
259 ret = facl (desc, SETACL,
260 sizeof (entries) / sizeof (aclent_t), entries);
261 else
262 ret = acl (name, SETACL,
263 sizeof (entries) / sizeof (aclent_t), entries);
264 if (ret < 0)
266 if (errno == ENOSYS || errno == EOPNOTSUPP)
268 *must_chmod = true;
269 return 0;
271 return -1;
273 return 0;
277 # elif HAVE_GETACL /* HP-UX */
278 static int
279 context_acl_from_mode (struct permission_context *ctx, const char *name, int desc)
281 struct stat statbuf;
282 int ret;
284 if (desc != -1)
285 ret = fstat (desc, &statbuf);
286 else
287 ret = stat (name, &statbuf);
288 if (ret < 0)
289 return -1;
291 ctx->entries[0].uid = statbuf.st_uid;
292 ctx->entries[0].gid = ACL_NSGROUP;
293 ctx->entries[0].mode = (ctx->mode >> 6) & 7;
294 ctx->entries[1].uid = ACL_NSUSER;
295 ctx->entries[1].gid = statbuf.st_gid;
296 ctx->entries[1].mode = (ctx->mode >> 3) & 7;
297 ctx->entries[2].uid = ACL_NSUSER;
298 ctx->entries[2].gid = ACL_NSGROUP;
299 ctx->entries[2].mode = ctx->mode & 7;
300 ctx->count = 3;
301 return 0;
304 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
305 static int
306 context_aclv_from_mode (struct permission_context *ctx)
308 int ret;
310 ctx->aclv_entries[0].a_type = USER_OBJ;
311 ctx->aclv_entries[0].a_id = 0; /* irrelevant */
312 ctx->aclv_entries[0].a_perm = (ctx->mode >> 6) & 7;
313 ctx->aclv_entries[1].a_type = GROUP_OBJ;
314 ctx->aclv_entries[1].a_id = 0; /* irrelevant */
315 ctx->aclv_entries[1].a_perm = (ctx->mode >> 3) & 7;
316 ctx->aclv_entries[2].a_type = CLASS_OBJ;
317 ctx->aclv_entries[2].a_id = 0;
318 ctx->aclv_entries[2].a_perm = (ctx->mode >> 3) & 7;
319 ctx->aclv_entries[3].a_type = OTHER_OBJ;
320 ctx->aclv_entries[3].a_id = 0;
321 ctx->aclv_entries[3].a_perm = ctx->mode & 7;
322 ctx->aclv_count = 4;
324 ret = aclsort (ctx->aclv_count, 1, ctx->aclv_entries);
325 if (ret > 0)
326 abort ();
327 return ret;
329 # endif
331 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
332 static int
333 set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
335 acl_type_list_t types;
336 size_t types_size = sizeof (types);
337 acl_type_t type;
339 if (aclx_gettypes (name, &types, &types_size) < 0
340 || types.num_entries == 0)
342 *must_chmod = true;
343 return 0;
346 /* XXX Do we need to clear all types of ACLs for the given file, or is it
347 sufficient to clear the first one? */
348 type = types.entries[0];
349 if (type.u64 == ACL_AIXC)
351 union { struct acl a; char room[128]; } u;
352 int ret;
354 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
355 u.a.acl_mode = mode & ~(S_IXACL | 0777);
356 u.a.u_access = (mode >> 6) & 7;
357 u.a.g_access = (mode >> 3) & 7;
358 u.a.o_access = mode & 7;
360 if (desc != -1)
361 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
362 type, &u.a, u.a.acl_len, mode);
363 else
364 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
365 type, &u.a, u.a.acl_len, mode);
366 if (!(ret < 0 && errno == ENOSYS))
367 return ret;
369 else if (type.u64 == ACL_NFS4)
371 union { nfs4_acl_int_t a; char room[128]; } u;
372 nfs4_ace_int_t *ace;
373 int ret;
375 u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
376 u.a.aclEntryN = 0;
377 ace = &u.a.aclEntry[0];
379 ace->flags = ACE4_ID_SPECIAL;
380 ace->aceWho.special_whoid = ACE4_WHO_OWNER;
381 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
382 ace->aceFlags = 0;
383 ace->aceMask =
384 (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
385 | (mode & 0200
386 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
387 | ACE4_ADD_SUBDIRECTORY
388 : 0)
389 | (mode & 0100 ? ACE4_EXECUTE : 0);
390 ace->aceWhoString[0] = '\0';
391 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
392 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
393 u.a.aclEntryN++;
396 ace->flags = ACE4_ID_SPECIAL;
397 ace->aceWho.special_whoid = ACE4_WHO_GROUP;
398 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
399 ace->aceFlags = 0;
400 ace->aceMask =
401 (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
402 | (mode & 0020
403 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
404 | ACE4_ADD_SUBDIRECTORY
405 : 0)
406 | (mode & 0010 ? ACE4_EXECUTE : 0);
407 ace->aceWhoString[0] = '\0';
408 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
409 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
410 u.a.aclEntryN++;
413 ace->flags = ACE4_ID_SPECIAL;
414 ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
415 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
416 ace->aceFlags = 0;
417 ace->aceMask =
418 (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
419 | (mode & 0002
420 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
421 | ACE4_ADD_SUBDIRECTORY
422 : 0)
423 | (mode & 0001 ? ACE4_EXECUTE : 0);
424 ace->aceWhoString[0] = '\0';
425 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
426 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
427 u.a.aclEntryN++;
429 u.a.aclLength = (char *) ace - (char *) &u.a;
431 if (desc != -1)
432 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
433 type, &u.a, u.a.aclLength, mode);
434 else
435 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
436 type, &u.a, u.a.aclLength, mode);
437 if (!(ret < 0 && errno == ENOSYS))
438 return ret;
441 *must_chmod = true;
442 return 0;
445 # elif HAVE_STATACL /* older AIX */
446 static int
447 context_acl_from_mode (struct permission_context *ctx)
449 ctx->u.a.acl_len = (char *) &ctx->u.a.acl_ext[0] - (char *) &ctx->u.a; /* no entries */
450 ctx->u.a.acl_mode = ctx->mode & ~(S_IXACL | 0777);
451 ctx->u.a.u_access = (ctx->mode >> 6) & 7;
452 ctx->u.a.g_access = (ctx->mode >> 3) & 7;
453 ctx->u.a.o_access = ctx->mode & 7;
454 ctx->have_u = true;
455 return 0;
458 # elif HAVE_ACLSORT /* NonStop Kernel */
459 static int
460 context_acl_from_mode (struct permission_context *ctx)
462 int ret;
464 ctx->entries[0].a_type = USER_OBJ;
465 ctx->entries[0].a_id = 0; /* irrelevant */
466 ctx->entries[0].a_perm = (ctx->mode >> 6) & 7;
467 ctx->entries[1].a_type = GROUP_OBJ;
468 ctx->entries[1].a_id = 0; /* irrelevant */
469 ctx->entries[1].a_perm = (ctx->mode >> 3) & 7;
470 ctx->entries[2].a_type = CLASS_OBJ;
471 ctx->entries[2].a_id = 0;
472 ctx->entries[2].a_perm = (ctx->mode >> 3) & 7;
473 ctx->entries[3].a_type = OTHER_OBJ;
474 ctx->entries[3].a_id = 0;
475 ctx->entries[3].a_perm = ctx->mode & 7;
476 ctx->count = 4;
478 ret = aclsort (ctx->count, 1, entries);
479 if (ret > 0)
480 abort ();
481 return ret;
483 # endif
485 static int
486 set_acls (struct permission_context *ctx, const char *name, int desc,
487 int from_mode, bool *must_chmod, bool *acls_set)
489 int ret = 0;
491 # if HAVE_ACL_GET_FILE
492 /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
493 /* Linux, FreeBSD, Mac OS X, IRIX, Tru64, Cygwin >= 2.5 */
494 # if !HAVE_ACL_TYPE_EXTENDED
495 /* Linux, FreeBSD, IRIX, Tru64, Cygwin >= 2.5 */
497 # ifndef HAVE_ACL_FROM_TEXT
498 # error Must have acl_from_text (see POSIX 1003.1e draft 17).
499 # endif
500 # ifndef HAVE_ACL_DELETE_DEF_FILE
501 # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
502 # endif
504 if (! ctx->acls_not_supported)
506 if (ret == 0 && from_mode)
508 if (ctx->acl)
509 acl_free (ctx->acl);
510 ctx->acl = acl_from_mode (ctx->mode);
511 if (ctx->acl == NULL)
512 ret = -1;
515 if (ret == 0 && ctx->acl)
517 if (HAVE_ACL_SET_FD && desc != -1)
518 ret = acl_set_fd (desc, ctx->acl);
519 else
520 ret = acl_set_file (name, ACL_TYPE_ACCESS, ctx->acl);
521 if (ret != 0)
523 if (! acl_errno_valid (errno))
525 ctx->acls_not_supported = true;
526 if (from_mode || acl_access_nontrivial (ctx->acl) == 0)
527 ret = 0;
530 else
532 *acls_set = true;
533 if (S_ISDIR(ctx->mode))
535 if (! from_mode && ctx->default_acl &&
536 acl_default_nontrivial (ctx->default_acl))
537 ret = acl_set_file (name, ACL_TYPE_DEFAULT,
538 ctx->default_acl);
539 else
540 ret = acl_delete_def_file (name);
546 # if HAVE_ACL_TYPE_NFS4 /* FreeBSD */
548 /* File systems either support POSIX ACLs (for example, ufs) or NFS4 ACLs
549 (for example, zfs). */
551 /* TODO: Implement setting ACLs once get_permissions() reads them. */
553 # endif
555 # else /* HAVE_ACL_TYPE_EXTENDED */
556 /* Mac OS X */
558 /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
559 and acl_get_file (name, ACL_TYPE_DEFAULT)
560 always return NULL / EINVAL. You have to use
561 acl_get_file (name, ACL_TYPE_EXTENDED)
562 or acl_get_fd (open (name, ...))
563 to retrieve an ACL.
564 On the other hand,
565 acl_set_file (name, ACL_TYPE_ACCESS, acl)
566 and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
567 have the same effect as
568 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
569 Each of these calls sets the file's ACL. */
571 if (ctx->acl == NULL)
573 acl_t acl;
575 /* Remove ACLs if the file has ACLs. */
576 if (HAVE_ACL_GET_FD && desc != -1)
577 acl = acl_get_fd (desc);
578 else
579 acl = acl_get_file (name, ACL_TYPE_EXTENDED);
580 if (acl)
582 acl_free (acl);
584 acl = acl_init (0);
585 if (acl)
587 if (HAVE_ACL_SET_FD && desc != -1)
588 ret = acl_set_fd (desc, acl);
589 else
590 ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
591 acl_free (acl);
593 else
594 ret = -1;
597 else
599 if (HAVE_ACL_SET_FD && desc != -1)
600 ret = acl_set_fd (desc, ctx->acl);
601 else
602 ret = acl_set_file (name, ACL_TYPE_EXTENDED, ctx->acl);
603 if (ret != 0)
605 if (! acl_errno_valid (errno)
606 && ! acl_extended_nontrivial (ctx->acl))
607 ret = 0;
610 *acls_set = true;
612 # endif
614 # elif defined GETACL /* Solaris, Cygwin, not HP-UX */
616 /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
617 of Unixware. The acl() call returns the access and default ACL both
618 at once. */
620 /* If both ace_entries and entries are available, try SETACL before
621 ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
622 can. */
624 if (from_mode)
625 return set_acls_from_mode (name, desc, ctx->mode, must_chmod);
627 if (ret == 0 && ctx->count)
629 if (desc != -1)
630 ret = facl (desc, SETACL, ctx->count, ctx->entries);
631 else
632 ret = acl (name, SETACL, ctx->count, ctx->entries);
633 if (ret < 0)
635 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
636 && acl_nontrivial (ctx->count, ctx->entries) == 0)
637 ret = 0;
639 else
640 *acls_set = true;
643 # ifdef ACE_GETACL
644 if (ret == 0 && ctx->ace_count)
646 if (desc != -1)
647 ret = facl (desc, ACE_SETACL, ctx->ace_count, ctx->ace_entries);
648 else
649 ret = acl (name, ACE_SETACL, ctx->ace_count, ctx->ace_entries);
650 if (ret < 0)
652 if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
653 && acl_ace_nontrivial (ctx->ace_count, ctx->ace_entries) == 0)
654 ret = 0;
656 else
657 *acls_set = true;
659 # endif
661 # elif HAVE_GETACL /* HP-UX */
663 if (from_mode)
664 ret = context_acl_from_mode (ctx, name, desc);
666 if (ret == 0 && ctx->count > 0)
668 if (desc != -1)
669 ret = fsetacl (desc, ctx->count, ctx->entries);
670 else
671 ret = setacl (name, ctx->count, ctx->entries);
672 if (ret < 0)
674 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
675 && (from_mode || !acl_nontrivial (ctx->count, ctx->entries)))
676 ret = 0;
678 else
679 *acls_set = true;
682 # if HAVE_ACLV_H
683 if (from_mode)
684 ret = context_aclv_from_mode (ctx);
686 if (ret == 0 && ctx->aclv_count > 0)
688 ret = acl ((char *) name, ACL_SET, ctx->aclv_count, ctx->aclv_entries);
689 if (ret < 0)
691 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
692 && (from_mode || !aclv_nontrivial (ctx->aclv_count, ctx->aclv_entries)))
693 ret = 0;
695 else
696 *acls_set = true;
698 # endif
700 # elif HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */
702 /* TODO: Implement setting ACLs once get_permissions() reads them. */
704 if (from_mode)
705 ret = set_acls_from_mode (name, desc, mode, must_chmod);
707 # elif HAVE_STATACL /* older AIX */
709 if (from_mode)
710 ret = context_acl_from_mode (ctx);
712 if (ret == 0 && ctx->have_u)
714 if (desc != -1)
715 ret = fchacl (desc, &ctx->u.a, ctx->u.a.acl_len);
716 else
717 ret = chacl ((char *) name, &ctx->u.a, ctx->u.a.acl_len);
718 if (ret < 0)
720 if (errno == ENOSYS && from_mode)
721 ret = 0;
723 else
724 *acls_set = true;
727 # elif HAVE_ACLSORT /* NonStop Kernel */
729 if (from_mode)
730 ret = context_acl_from_mode (ctx);
732 if (ret == 0 && ctx->count)
734 ret = acl ((char *) name, ACL_SET, ctx->count, ctx->entries);
735 if (ret != 0)
737 if (!acl_nontrivial (ctx->count, ctx->entries))
738 ret = 0;
740 else
741 *acls_set = true;
744 # else /* No ACLs */
746 /* Nothing to do. */
748 # endif
750 return ret;
752 #endif
754 /* If DESC is a valid file descriptor use fchmod to change the
755 file's mode to MODE on systems that have fchmod. On systems
756 that don't have fchmod and if DESC is invalid, use chmod on
757 NAME instead.
758 Return 0 if successful. Return -1 and set errno upon failure. */
761 chmod_or_fchmod (const char *name, int desc, mode_t mode)
763 if (HAVE_FCHMOD && desc != -1)
764 return fchmod (desc, mode);
765 else
766 return chmod (name, mode);
769 /* Set the permissions in CTX on a file. If DESC is a valid file descriptor,
770 use file descriptor operations, else use filename based operations on NAME.
771 If access control lists are not available, fchmod the target file to the
772 mode in CTX. Also sets the non-permission bits of the destination file
773 (S_ISUID, S_ISGID, S_ISVTX) to those from the mode in CTX if any are set.
774 Return 0 if successful. Return -1 and set errno upon failure. */
777 set_permissions (struct permission_context *ctx, const char *name, int desc)
779 _GL_UNUSED bool acls_set = false;
780 bool early_chmod;
781 bool must_chmod = false;
782 int ret = 0;
784 #if USE_ACL
785 # if HAVE_STATACL
786 /* older AIX */
787 /* There is no need to call chmod_or_fchmod, since the mode
788 bits S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */
790 early_chmod = false;
791 # else
792 /* All other platforms */
793 /* On Cygwin, it is necessary to call chmod before acl, because
794 chmod can change the contents of the ACL (in ways that don't
795 change the allowed accesses, but still visible). */
797 early_chmod = (! MODE_INSIDE_ACL || (ctx->mode & (S_ISUID | S_ISGID | S_ISVTX)));
798 # endif
799 #else
800 /* No ACLs */
802 early_chmod = true;
803 #endif
805 if (early_chmod)
807 ret = chmod_or_fchmod (name, desc, ctx->mode);
808 if (ret != 0)
809 return -1;
812 #if USE_ACL
813 ret = set_acls (ctx, name, desc, false, &must_chmod, &acls_set);
814 if (! acls_set)
816 int saved_errno = ret ? errno : 0;
818 /* If we can't set an acl which we expect to be able to set, try setting
819 the permissions to ctx->mode. Due to possible inherited permissions,
820 we cannot simply chmod. */
822 ret = set_acls (ctx, name, desc, true, &must_chmod, &acls_set);
823 if (! acls_set)
824 must_chmod = true;
826 if (saved_errno)
828 errno = saved_errno;
829 ret = -1;
832 #endif
834 if (must_chmod && ! early_chmod)
836 int saved_errno = ret ? errno : 0;
838 ret = chmod_or_fchmod (name, desc, ctx->mode);
840 if (saved_errno)
842 errno = saved_errno;
843 ret = -1;
847 return ret;