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