1 /* Set permissions of a file. -*- coding: utf-8 -*-
2
3 Copyright (C) 2002-2003, 2005-2018 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 <https://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
acl_from_mode(mode_t mode)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
set_acls_from_mode(const char * name,int desc,mode_t mode,bool * must_chmod)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 return 0;
273 }
274 }
275
276 # elif HAVE_GETACL /* HP-UX */
277 static int
context_acl_from_mode(struct permission_context * ctx,const char * name,int desc)278 context_acl_from_mode (struct permission_context *ctx, const char *name, int desc)
279 {
280 struct stat statbuf;
281 int ret;
282
283 if (desc != -1)
284 ret = fstat (desc, &statbuf);
285 else
286 ret = stat (name, &statbuf);
287 if (ret < 0)
288 return -1;
289
290 ctx->entries[0].uid = statbuf.st_uid;
291 ctx->entries[0].gid = ACL_NSGROUP;
292 ctx->entries[0].mode = (ctx->mode >> 6) & 7;
293 ctx->entries[1].uid = ACL_NSUSER;
294 ctx->entries[1].gid = statbuf.st_gid;
295 ctx->entries[1].mode = (ctx->mode >> 3) & 7;
296 ctx->entries[2].uid = ACL_NSUSER;
297 ctx->entries[2].gid = ACL_NSGROUP;
298 ctx->entries[2].mode = ctx->mode & 7;
299 ctx->count = 3;
300 return 0;
301 }
302
303 # if HAVE_ACLV_H /* HP-UX >= 11.11 */
304 static int
context_aclv_from_mode(struct permission_context * ctx)305 context_aclv_from_mode (struct permission_context *ctx)
306 {
307 int ret;
308
309 ctx->aclv_entries[0].a_type = USER_OBJ;
310 ctx->aclv_entries[0].a_id = 0; /* irrelevant */
311 ctx->aclv_entries[0].a_perm = (ctx->mode >> 6) & 7;
312 ctx->aclv_entries[1].a_type = GROUP_OBJ;
313 ctx->aclv_entries[1].a_id = 0; /* irrelevant */
314 ctx->aclv_entries[1].a_perm = (ctx->mode >> 3) & 7;
315 ctx->aclv_entries[2].a_type = CLASS_OBJ;
316 ctx->aclv_entries[2].a_id = 0;
317 ctx->aclv_entries[2].a_perm = (ctx->mode >> 3) & 7;
318 ctx->aclv_entries[3].a_type = OTHER_OBJ;
319 ctx->aclv_entries[3].a_id = 0;
320 ctx->aclv_entries[3].a_perm = ctx->mode & 7;
321 ctx->aclv_count = 4;
322
323 ret = aclsort (ctx->aclv_count, 1, ctx->aclv_entries);
324 if (ret > 0)
325 abort ();
326 return ret;
327 }
328 # endif
329
330 # elif HAVE_ACLX_GET && defined ACL_AIX_WIP /* AIX */
331 static int
set_acls_from_mode(const char * name,int desc,mode_t mode,bool * must_chmod)332 set_acls_from_mode (const char *name, int desc, mode_t mode, bool *must_chmod)
333 {
334 acl_type_list_t types;
335 size_t types_size = sizeof (types);
336 acl_type_t type;
337
338 if (aclx_gettypes (name, &types, &types_size) < 0
339 || types.num_entries == 0)
340 {
341 *must_chmod = true;
342 return 0;
343 }
344
345 /* XXX Do we need to clear all types of ACLs for the given file, or is it
346 sufficient to clear the first one? */
347 type = types.entries[0];
348 if (type.u64 == ACL_AIXC)
349 {
350 union { struct acl a; char room[128]; } u;
351 int ret;
352
353 u.a.acl_len = (char *) &u.a.acl_ext[0] - (char *) &u.a; /* no entries */
354 u.a.acl_mode = mode & ~(S_IXACL | 0777);
355 u.a.u_access = (mode >> 6) & 7;
356 u.a.g_access = (mode >> 3) & 7;
357 u.a.o_access = mode & 7;
358
359 if (desc != -1)
360 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
361 type, &u.a, u.a.acl_len, mode);
362 else
363 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
364 type, &u.a, u.a.acl_len, mode);
365 if (!(ret < 0 && errno == ENOSYS))
366 return ret;
367 }
368 else if (type.u64 == ACL_NFS4)
369 {
370 union { nfs4_acl_int_t a; char room[128]; } u;
371 nfs4_ace_int_t *ace;
372 int ret;
373
374 u.a.aclVersion = NFS4_ACL_INT_STRUCT_VERSION;
375 u.a.aclEntryN = 0;
376 ace = &u.a.aclEntry[0];
377 {
378 ace->flags = ACE4_ID_SPECIAL;
379 ace->aceWho.special_whoid = ACE4_WHO_OWNER;
380 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
381 ace->aceFlags = 0;
382 ace->aceMask =
383 (mode & 0400 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
384 | (mode & 0200
385 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
386 | ACE4_ADD_SUBDIRECTORY
387 : 0)
388 | (mode & 0100 ? ACE4_EXECUTE : 0);
389 ace->aceWhoString[0] = '\0';
390 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
391 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
392 u.a.aclEntryN++;
393 }
394 {
395 ace->flags = ACE4_ID_SPECIAL;
396 ace->aceWho.special_whoid = ACE4_WHO_GROUP;
397 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
398 ace->aceFlags = 0;
399 ace->aceMask =
400 (mode & 0040 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
401 | (mode & 0020
402 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
403 | ACE4_ADD_SUBDIRECTORY
404 : 0)
405 | (mode & 0010 ? ACE4_EXECUTE : 0);
406 ace->aceWhoString[0] = '\0';
407 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
408 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
409 u.a.aclEntryN++;
410 }
411 {
412 ace->flags = ACE4_ID_SPECIAL;
413 ace->aceWho.special_whoid = ACE4_WHO_EVERYONE;
414 ace->aceType = ACE4_ACCESS_ALLOWED_ACE_TYPE;
415 ace->aceFlags = 0;
416 ace->aceMask =
417 (mode & 0004 ? ACE4_READ_DATA | ACE4_LIST_DIRECTORY : 0)
418 | (mode & 0002
419 ? ACE4_WRITE_DATA | ACE4_ADD_FILE | ACE4_APPEND_DATA
420 | ACE4_ADD_SUBDIRECTORY
421 : 0)
422 | (mode & 0001 ? ACE4_EXECUTE : 0);
423 ace->aceWhoString[0] = '\0';
424 ace->entryLen = (char *) &ace->aceWhoString[4] - (char *) ace;
425 ace = (nfs4_ace_int_t *) (char *) &ace->aceWhoString[4];
426 u.a.aclEntryN++;
427 }
428 u.a.aclLength = (char *) ace - (char *) &u.a;
429
430 if (desc != -1)
431 ret = aclx_fput (desc, SET_ACL | SET_MODE_S_BITS,
432 type, &u.a, u.a.aclLength, mode);
433 else
434 ret = aclx_put (name, SET_ACL | SET_MODE_S_BITS,
435 type, &u.a, u.a.aclLength, mode);
436 if (!(ret < 0 && errno == ENOSYS))
437 return ret;
438 }
439
440 *must_chmod = true;
441 return 0;
442 }
443
444 # elif HAVE_STATACL /* older AIX */
445 static int
context_acl_from_mode(struct permission_context * ctx)446 context_acl_from_mode (struct permission_context *ctx)
447 {
448 ctx->u.a.acl_len = (char *) &ctx->u.a.acl_ext[0] - (char *) &ctx->u.a; /* no entries */
449 ctx->u.a.acl_mode = ctx->mode & ~(S_IXACL | 0777);
450 ctx->u.a.u_access = (ctx->mode >> 6) & 7;
451 ctx->u.a.g_access = (ctx->mode >> 3) & 7;
452 ctx->u.a.o_access = ctx->mode & 7;
453 ctx->have_u = true;
454 return 0;
455 }
456
457 # elif HAVE_ACLSORT /* NonStop Kernel */
458 static int
context_acl_from_mode(struct permission_context * ctx)459 context_acl_from_mode (struct permission_context *ctx)
460 {
461 int ret;
462
463 ctx->entries[0].a_type = USER_OBJ;
464 ctx->entries[0].a_id = 0; /* irrelevant */
465 ctx->entries[0].a_perm = (ctx->mode >> 6) & 7;
466 ctx->entries[1].a_type = GROUP_OBJ;
467 ctx->entries[1].a_id = 0; /* irrelevant */
468 ctx->entries[1].a_perm = (ctx->mode >> 3) & 7;
469 ctx->entries[2].a_type = CLASS_OBJ;
470 ctx->entries[2].a_id = 0;
471 ctx->entries[2].a_perm = (ctx->mode >> 3) & 7;
472 ctx->entries[3].a_type = OTHER_OBJ;
473 ctx->entries[3].a_id = 0;
474 ctx->entries[3].a_perm = ctx->mode & 7;
475 ctx->count = 4;
476
477 ret = aclsort (ctx->count, 1, entries);
478 if (ret > 0)
479 abort ();
480 return ret;
481 }
482 # endif
483
484 static int
set_acls(struct permission_context * ctx,const char * name,int desc,int from_mode,bool * must_chmod,bool * acls_set)485 set_acls (struct permission_context *ctx, const char *name, int desc,
486 int from_mode, bool *must_chmod, bool *acls_set)
487 {
488 int ret = 0;
489
490 # if HAVE_ACL_GET_FILE
491 /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
492 /* Linux, FreeBSD, Mac OS X, IRIX, Tru64 */
493 # if !HAVE_ACL_TYPE_EXTENDED
494 /* Linux, FreeBSD, IRIX, Tru64 */
495
496 # ifndef HAVE_ACL_FROM_TEXT
497 # error Must have acl_from_text (see POSIX 1003.1e draft 17).
498 # endif
499 # ifndef HAVE_ACL_DELETE_DEF_FILE
500 # error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
501 # endif
502
503 if (! ctx->acls_not_supported)
504 {
505 if (ret == 0 && from_mode)
506 {
507 if (ctx->acl)
508 acl_free (ctx->acl);
509 ctx->acl = acl_from_mode (ctx->mode);
510 if (ctx->acl == NULL)
511 ret = -1;
512 }
513
514 if (ret == 0 && ctx->acl)
515 {
516 if (HAVE_ACL_SET_FD && desc != -1)
517 ret = acl_set_fd (desc, ctx->acl);
518 else
519 ret = acl_set_file (name, ACL_TYPE_ACCESS, ctx->acl);
520 if (ret != 0)
521 {
522 if (! acl_errno_valid (errno))
523 {
524 ctx->acls_not_supported = true;
525 if (from_mode || acl_access_nontrivial (ctx->acl) == 0)
526 ret = 0;
527 }
528 }
529 else
530 {
531 *acls_set = true;
532 if (S_ISDIR(ctx->mode))
533 {
534 if (! from_mode && ctx->default_acl &&
535 acl_default_nontrivial (ctx->default_acl))
536 ret = acl_set_file (name, ACL_TYPE_DEFAULT,
537 ctx->default_acl);
538 else
539 ret = acl_delete_def_file (name);
540 }
541 }
542 }
543 }
544
545 # if HAVE_ACL_TYPE_NFS4 /* FreeBSD */
546
547 /* File systems either support POSIX ACLs (for example, ufs) or NFS4 ACLs
548 (for example, zfs). */
549
550 /* TODO: Implement setting ACLs once get_permissions() reads them. */
551
552 # endif
553
554 # else /* HAVE_ACL_TYPE_EXTENDED */
555 /* Mac OS X */
556
557 /* On Mac OS X, acl_get_file (name, ACL_TYPE_ACCESS)
558 and acl_get_file (name, ACL_TYPE_DEFAULT)
559 always return NULL / EINVAL. You have to use
560 acl_get_file (name, ACL_TYPE_EXTENDED)
561 or acl_get_fd (open (name, ...))
562 to retrieve an ACL.
563 On the other hand,
564 acl_set_file (name, ACL_TYPE_ACCESS, acl)
565 and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
566 have the same effect as
567 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
568 Each of these calls sets the file's ACL. */
569
570 if (ctx->acl == NULL)
571 {
572 acl_t acl;
573
574 /* Remove ACLs if the file has ACLs. */
575 if (HAVE_ACL_GET_FD && desc != -1)
576 acl = acl_get_fd (desc);
577 else
578 acl = acl_get_file (name, ACL_TYPE_EXTENDED);
579 if (acl)
580 {
581 acl_free (acl);
582
583 acl = acl_init (0);
584 if (acl)
585 {
586 if (HAVE_ACL_SET_FD && desc != -1)
587 ret = acl_set_fd (desc, acl);
588 else
589 ret = acl_set_file (name, ACL_TYPE_EXTENDED, acl);
590 acl_free (acl);
591 }
592 else
593 ret = -1;
594 }
595 }
596 else
597 {
598 if (HAVE_ACL_SET_FD && desc != -1)
599 ret = acl_set_fd (desc, ctx->acl);
600 else
601 ret = acl_set_file (name, ACL_TYPE_EXTENDED, ctx->acl);
602 if (ret != 0)
603 {
604 if (! acl_errno_valid (errno)
605 && ! acl_extended_nontrivial (ctx->acl))
606 ret = 0;
607 }
608 }
609 *acls_set = true;
610
611 # endif
612
613 # elif defined GETACL /* Solaris, Cygwin, not HP-UX */
614
615 /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
616 of Unixware. The acl() call returns the access and default ACL both
617 at once. */
618
619 /* If both ace_entries and entries are available, try SETACL before
620 ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
621 can. */
622
623 if (from_mode)
624 return set_acls_from_mode (name, desc, ctx->mode, must_chmod);
625
626 if (ret == 0 && ctx->count)
627 {
628 if (desc != -1)
629 ret = facl (desc, SETACL, ctx->count, ctx->entries);
630 else
631 ret = acl (name, SETACL, ctx->count, ctx->entries);
632 if (ret < 0)
633 {
634 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
635 && acl_nontrivial (ctx->count, ctx->entries) == 0)
636 ret = 0;
637 }
638 else
639 *acls_set = true;
640 }
641
642 # ifdef ACE_GETACL
643 if (ret == 0 && ctx->ace_count)
644 {
645 if (desc != -1)
646 ret = facl (desc, ACE_SETACL, ctx->ace_count, ctx->ace_entries);
647 else
648 ret = acl (name, ACE_SETACL, ctx->ace_count, ctx->ace_entries);
649 if (ret < 0)
650 {
651 if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
652 && acl_ace_nontrivial (ctx->ace_count, ctx->ace_entries) == 0)
653 ret = 0;
654 }
655 else
656 *acls_set = true;
657 }
658 # endif
659
660 # elif HAVE_GETACL /* HP-UX */
661
662 if (from_mode)
663 ret = context_acl_from_mode (ctx, name, desc);
664
665 if (ret == 0 && ctx->count > 0)
666 {
667 if (desc != -1)
668 ret = fsetacl (desc, ctx->count, ctx->entries);
669 else
670 ret = setacl (name, ctx->count, ctx->entries);
671 if (ret < 0)
672 {
673 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == ENOTSUP)
674 && (from_mode || !acl_nontrivial (ctx->count, ctx->entries)))
675 ret = 0;
676 }
677 else
678 *acls_set = true;
679 }
680
681 # if HAVE_ACLV_H
682 if (from_mode)
683 ret = context_aclv_from_mode (ctx);
684
685 if (ret == 0 && ctx->aclv_count > 0)
686 {
687 ret = acl ((char *) name, ACL_SET, ctx->aclv_count, ctx->aclv_entries);
688 if (ret < 0)
689 {
690 if ((errno == ENOSYS || errno == EOPNOTSUPP || errno == EINVAL)
691 && (from_mode || !aclv_nontrivial (ctx->aclv_count, ctx->aclv_entries)))
692 ret = 0;
693 }
694 else
695 *acls_set = true;
696 }
697 # endif
698
699 # elif HAVE_ACLX_GET && ACL_AIX_WIP /* AIX */
700
701 /* TODO: Implement setting ACLs once get_permissions() reads them. */
702
703 if (from_mode)
704 ret = set_acls_from_mode (name, desc, mode, must_chmod);
705
706 # elif HAVE_STATACL /* older AIX */
707
708 if (from_mode)
709 ret = context_acl_from_mode (ctx);
710
711 if (ret == 0 && ctx->have_u)
712 {
713 if (desc != -1)
714 ret = fchacl (desc, &ctx->u.a, ctx->u.a.acl_len);
715 else
716 ret = chacl ((char *) name, &ctx->u.a, ctx->u.a.acl_len);
717 if (ret < 0)
718 {
719 if (errno == ENOSYS && from_mode)
720 ret = 0;
721 }
722 else
723 *acls_set = true;
724 }
725
726 # elif HAVE_ACLSORT /* NonStop Kernel */
727
728 if (from_mode)
729 ret = context_acl_from_mode (ctx);
730
731 if (ret == 0 && ctx->count)
732 {
733 ret = acl ((char *) name, ACL_SET, ctx->count, ctx->entries);
734 if (ret != 0)
735 {
736 if (!acl_nontrivial (ctx->count, ctx->entries))
737 ret = 0;
738 }
739 else
740 *acls_set = true;
741 }
742
743 # else /* No ACLs */
744
745 /* Nothing to do. */
746
747 # endif
748
749 return ret;
750 }
751 #endif
752
753 /* If DESC is a valid file descriptor use fchmod to change the
754 file's mode to MODE on systems that have fchmod. On systems
755 that don't have fchmod and if DESC is invalid, use chmod on
756 NAME instead.
757 Return 0 if successful. Return -1 and set errno upon failure. */
758
759 int
chmod_or_fchmod(const char * name,int desc,mode_t mode)760 chmod_or_fchmod (const char *name, int desc, mode_t mode)
761 {
762 if (HAVE_FCHMOD && desc != -1)
763 return fchmod (desc, mode);
764 else
765 return chmod (name, mode);
766 }
767
768 /* Set the permissions in CTX on a file. If DESC is a valid file descriptor,
769 use file descriptor operations, else use filename based operations on NAME.
770 If access control lists are not available, fchmod the target file to the
771 mode in CTX. Also sets the non-permission bits of the destination file
772 (S_ISUID, S_ISGID, S_ISVTX) to those from the mode in CTX if any are set.
773 Return 0 if successful. Return -1 and set errno upon failure. */
774
775 int
set_permissions(struct permission_context * ctx,const char * name,int desc)776 set_permissions (struct permission_context *ctx, const char *name, int desc)
777 {
778 bool acls_set _GL_UNUSED = false;
779 bool early_chmod;
780 bool must_chmod = false;
781 int ret = 0;
782
783 #if USE_ACL
784 # if HAVE_STATACL
785 /* older AIX */
786 /* There is no need to call chmod_or_fchmod, since the mode
787 bits S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */
788
789 early_chmod = false;
790 # else
791 /* All other platforms */
792 /* On Cygwin, it is necessary to call chmod before acl, because
793 chmod can change the contents of the ACL (in ways that don't
794 change the allowed accesses, but still visible). */
795
796 early_chmod = (! MODE_INSIDE_ACL || (ctx->mode & (S_ISUID | S_ISGID | S_ISVTX)));
797 # endif
798 #else
799 /* No ACLs */
800
801 early_chmod = true;
802 #endif
803
804 if (early_chmod)
805 {
806 ret = chmod_or_fchmod (name, desc, ctx->mode);
807 if (ret != 0)
808 return -1;
809 }
810
811 #if USE_ACL
812 ret = set_acls (ctx, name, desc, false, &must_chmod, &acls_set);
813 if (! acls_set)
814 {
815 int saved_errno = ret ? errno : 0;
816
817 /* If we can't set an acl which we expect to be able to set, try setting
818 the permissions to ctx->mode. Due to possible inherited permissions,
819 we cannot simply chmod. */
820
821 ret = set_acls (ctx, name, desc, true, &must_chmod, &acls_set);
822 if (! acls_set)
823 must_chmod = true;
824
825 if (saved_errno)
826 {
827 errno = saved_errno;
828 ret = -1;
829 }
830 }
831 #endif
832
833 if (must_chmod && ! early_chmod)
834 {
835 int saved_errno = ret ? errno : 0;
836
837 ret = chmod_or_fchmod (name, desc, ctx->mode);
838
839 if (saved_errno)
840 {
841 errno = saved_errno;
842 ret = -1;
843 }
844 }
845
846 return ret;
847 }
848