1 /* copy-acl.c - copy access control list from one file to another file
2
3 Copyright (C) 2002-2003, 2005-2008 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 #include "gettext.h"
27 #define _(msgid) gettext (msgid)
28
29
30 /* Copy access control lists from one file to another. If SOURCE_DESC is
31 a valid file descriptor, use file descriptor operations, else use
32 filename based operations on SRC_NAME. Likewise for DEST_DESC and
33 DST_NAME.
34 If access control lists are not available, fchmod the target file to
35 MODE. Also sets the non-permission bits of the destination file
36 (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
37 Return 0 if successful.
38 Return -2 and set errno for an error relating to the source file.
39 Return -1 and set errno for an error relating to the destination file. */
40
41 static int
qcopy_acl(const char * src_name,int source_desc,const char * dst_name,int dest_desc,mode_t mode)42 qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
43 int dest_desc, mode_t mode)
44 {
45 #if USE_ACL && HAVE_ACL_GET_FILE
46 /* POSIX 1003.1e (draft 17 -- abandoned) specific version. */
47 /* Linux, FreeBSD, MacOS X, IRIX, Tru64 */
48 # if MODE_INSIDE_ACL
49 /* Linux, FreeBSD, IRIX, Tru64 */
50
51 acl_t acl;
52 int ret;
53
54 if (HAVE_ACL_GET_FD && source_desc != -1)
55 acl = acl_get_fd (source_desc);
56 else
57 acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
58 if (acl == NULL)
59 {
60 if (ACL_NOT_WELL_SUPPORTED (errno))
61 return qset_acl (dst_name, dest_desc, mode);
62 else
63 return -2;
64 }
65
66 if (HAVE_ACL_SET_FD && dest_desc != -1)
67 ret = acl_set_fd (dest_desc, acl);
68 else
69 ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
70 if (ret != 0)
71 {
72 int saved_errno = errno;
73
74 if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_access_nontrivial (acl))
75 {
76 acl_free (acl);
77 return chmod_or_fchmod (dst_name, dest_desc, mode);
78 }
79 else
80 {
81 acl_free (acl);
82 chmod_or_fchmod (dst_name, dest_desc, mode);
83 errno = saved_errno;
84 return -1;
85 }
86 }
87 else
88 acl_free (acl);
89
90 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
91 {
92 /* We did not call chmod so far, and either the mode and the ACL are
93 separate or special bits are to be set which don't fit into ACLs. */
94
95 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
96 return -1;
97 }
98
99 if (S_ISDIR (mode))
100 {
101 acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
102 if (acl == NULL)
103 return -2;
104
105 if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
106 {
107 int saved_errno = errno;
108
109 acl_free (acl);
110 errno = saved_errno;
111 return -1;
112 }
113 else
114 acl_free (acl);
115 }
116 return 0;
117
118 # else /* !MODE_INSIDE_ACL */
119 /* MacOS X */
120
121 # if !HAVE_ACL_TYPE_EXTENDED
122 # error Must have ACL_TYPE_EXTENDED
123 # endif
124
125 /* On MacOS X, acl_get_file (name, ACL_TYPE_ACCESS)
126 and acl_get_file (name, ACL_TYPE_DEFAULT)
127 always return NULL / EINVAL. You have to use
128 acl_get_file (name, ACL_TYPE_EXTENDED)
129 or acl_get_fd (open (name, ...))
130 to retrieve an ACL.
131 On the other hand,
132 acl_set_file (name, ACL_TYPE_ACCESS, acl)
133 and acl_set_file (name, ACL_TYPE_DEFAULT, acl)
134 have the same effect as
135 acl_set_file (name, ACL_TYPE_EXTENDED, acl):
136 Each of these calls sets the file's ACL. */
137
138 acl_t acl;
139 int ret;
140
141 if (HAVE_ACL_GET_FD && source_desc != -1)
142 acl = acl_get_fd (source_desc);
143 else
144 acl = acl_get_file (src_name, ACL_TYPE_EXTENDED);
145 if (acl == NULL)
146 {
147 if (ACL_NOT_WELL_SUPPORTED (errno))
148 return qset_acl (dst_name, dest_desc, mode);
149 else
150 return -2;
151 }
152
153 if (HAVE_ACL_SET_FD && dest_desc != -1)
154 ret = acl_set_fd (dest_desc, acl);
155 else
156 ret = acl_set_file (dst_name, ACL_TYPE_EXTENDED, acl);
157 if (ret != 0)
158 {
159 int saved_errno = errno;
160
161 if (ACL_NOT_WELL_SUPPORTED (errno) && !acl_extended_nontrivial (acl))
162 {
163 acl_free (acl);
164 return chmod_or_fchmod (dst_name, dest_desc, mode);
165 }
166 else
167 {
168 acl_free (acl);
169 chmod_or_fchmod (dst_name, dest_desc, mode);
170 errno = saved_errno;
171 return -1;
172 }
173 }
174 else
175 acl_free (acl);
176
177 /* Since !MODE_INSIDE_ACL, we have to call chmod explicitly. */
178 return chmod_or_fchmod (dst_name, dest_desc, mode);
179
180 # endif
181
182 #elif USE_ACL && defined GETACL /* Solaris, Cygwin, not HP-UX */
183
184 # if defined ACL_NO_TRIVIAL
185 /* Solaris 10 (newer version), which has additional API declared in
186 <sys/acl.h> (acl_t) and implemented in libsec (acl_set, acl_trivial,
187 acl_fromtext, ...). */
188
189 int ret;
190 acl_t *aclp = NULL;
191 ret = (source_desc < 0
192 ? acl_get (src_name, ACL_NO_TRIVIAL, &aclp)
193 : facl_get (source_desc, ACL_NO_TRIVIAL, &aclp));
194 if (ret != 0 && errno != ENOSYS)
195 return -2;
196
197 ret = qset_acl (dst_name, dest_desc, mode);
198 if (ret != 0)
199 return -1;
200
201 if (aclp)
202 {
203 ret = (dest_desc < 0
204 ? acl_set (dst_name, aclp)
205 : facl_set (dest_desc, aclp));
206 if (ret != 0)
207 {
208 int saved_errno = errno;
209
210 acl_free (aclp);
211 errno = saved_errno;
212 return -1;
213 }
214 acl_free (aclp);
215 }
216
217 return 0;
218
219 # else /* Solaris, Cygwin, general case */
220
221 /* Solaris 2.5 through Solaris 10, Cygwin, and contemporaneous versions
222 of Unixware. The acl() call returns the access and default ACL both
223 at once. */
224 # ifdef ACE_GETACL
225 int ace_count;
226 ace_t *ace_entries;
227 # endif
228 int count;
229 aclent_t *entries;
230 int did_chmod;
231 int saved_errno;
232 int ret;
233
234 # ifdef ACE_GETACL
235 /* Solaris also has a different variant of ACLs, used in ZFS and NFSv4
236 file systems (whereas the other ones are used in UFS file systems).
237 There is an API
238 pathconf (name, _PC_ACL_ENABLED)
239 fpathconf (desc, _PC_ACL_ENABLED)
240 that allows to determine which of the two kinds of ACLs is supported
241 for the given file. But some file systems may implement this call
242 incorrectly, so better not use it.
243 When fetching the source ACL, we simply fetch both ACL types.
244 When setting the destination ACL, we try either ACL types, assuming
245 that the kernel will translate the ACL from one form to the other.
246 (See in <http://docs.sun.com/app/docs/doc/819-2241/6n4huc7ia?l=en&a=view>
247 the description of ENOTSUP.) */
248 for (;;)
249 {
250 ace_count = (source_desc != -1
251 ? facl (source_desc, ACE_GETACLCNT, 0, NULL)
252 : acl (src_name, ACE_GETACLCNT, 0, NULL));
253
254 if (ace_count < 0)
255 {
256 if (errno == ENOSYS || errno == EINVAL)
257 {
258 ace_count = 0;
259 ace_entries = NULL;
260 break;
261 }
262 else
263 return -2;
264 }
265
266 if (ace_count == 0)
267 {
268 ace_entries = NULL;
269 break;
270 }
271
272 ace_entries = (ace_t *) malloc (ace_count * sizeof (ace_t));
273 if (ace_entries == NULL)
274 {
275 errno = ENOMEM;
276 return -2;
277 }
278
279 if ((source_desc != -1
280 ? facl (source_desc, ACE_GETACL, ace_count, ace_entries)
281 : acl (src_name, ACE_GETACL, ace_count, ace_entries))
282 == ace_count)
283 break;
284 /* Huh? The number of ACL entries changed since the last call.
285 Repeat. */
286 }
287 # endif
288
289 for (;;)
290 {
291 count = (source_desc != -1
292 ? facl (source_desc, GETACLCNT, 0, NULL)
293 : acl (src_name, GETACLCNT, 0, NULL));
294
295 if (count < 0)
296 {
297 if (errno == ENOSYS || errno == ENOTSUP)
298 {
299 count = 0;
300 entries = NULL;
301 break;
302 }
303 else
304 return -2;
305 }
306
307 if (count == 0)
308 {
309 entries = NULL;
310 break;
311 }
312
313 entries = (aclent_t *) malloc (count * sizeof (aclent_t));
314 if (entries == NULL)
315 {
316 errno = ENOMEM;
317 return -2;
318 }
319
320 if ((source_desc != -1
321 ? facl (source_desc, GETACL, count, entries)
322 : acl (src_name, GETACL, count, entries))
323 == count)
324 break;
325 /* Huh? The number of ACL entries changed since the last call.
326 Repeat. */
327 }
328
329 /* Is there an ACL of either kind? */
330 # ifdef ACE_GETACL
331 if (ace_count == 0)
332 # endif
333 if (count == 0)
334 return qset_acl (dst_name, dest_desc, mode);
335
336 did_chmod = 0; /* set to 1 once the mode bits in 0777 have been set */
337 saved_errno = 0; /* the first non-ignorable error code */
338
339 if (!MODE_INSIDE_ACL)
340 {
341 /* On Cygwin, it is necessary to call chmod before acl, because
342 chmod can change the contents of the ACL (in ways that don't
343 change the allowed accesses, but still visible). */
344 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
345 saved_errno = errno;
346 did_chmod = 1;
347 }
348
349 /* If both ace_entries and entries are available, try SETACL before
350 ACE_SETACL, because SETACL cannot fail with ENOTSUP whereas ACE_SETACL
351 can. */
352
353 if (count > 0)
354 {
355 ret = (dest_desc != -1
356 ? facl (dest_desc, SETACL, count, entries)
357 : acl (dst_name, SETACL, count, entries));
358 if (ret < 0 && saved_errno == 0)
359 {
360 saved_errno = errno;
361 if (errno == ENOSYS && !acl_nontrivial (count, entries))
362 saved_errno = 0;
363 }
364 else
365 did_chmod = 1;
366 }
367 free (entries);
368
369 # ifdef ACE_GETACL
370 if (ace_count > 0)
371 {
372 ret = (dest_desc != -1
373 ? facl (dest_desc, ACE_SETACL, ace_count, ace_entries)
374 : acl (dst_name, ACE_SETACL, ace_count, ace_entries));
375 if (ret < 0 && saved_errno == 0)
376 {
377 saved_errno = errno;
378 if ((errno == ENOSYS || errno == EINVAL || errno == ENOTSUP)
379 && !acl_ace_nontrivial (ace_count, ace_entries))
380 saved_errno = 0;
381 }
382 }
383 free (ace_entries);
384 # endif
385
386 if (MODE_INSIDE_ACL
387 && did_chmod <= ((mode & (S_ISUID | S_ISGID | S_ISVTX)) ? 1 : 0))
388 {
389 /* We did not call chmod so far, and either the mode and the ACL are
390 separate or special bits are to be set which don't fit into ACLs. */
391
392 if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
393 {
394 if (saved_errno == 0)
395 saved_errno = errno;
396 }
397 }
398
399 if (saved_errno)
400 {
401 errno = saved_errno;
402 return -1;
403 }
404 return 0;
405
406 # endif
407
408 #elif USE_ACL && HAVE_GETACL /* HP-UX */
409
410 int count;
411 struct acl_entry entries[NACLENTRIES];
412 int ret;
413
414 for (;;)
415 {
416 count = (source_desc != -1
417 ? fgetacl (source_desc, 0, NULL)
418 : getacl (src_name, 0, NULL));
419
420 if (count < 0)
421 {
422 if (errno == ENOSYS || errno == EOPNOTSUPP)
423 {
424 count = 0;
425 break;
426 }
427 else
428 return -2;
429 }
430
431 if (count == 0)
432 break;
433
434 if (count > NACLENTRIES)
435 /* If NACLENTRIES cannot be trusted, use dynamic memory allocation. */
436 abort ();
437
438 if ((source_desc != -1
439 ? fgetacl (source_desc, count, entries)
440 : getacl (src_name, count, entries))
441 == count)
442 break;
443 /* Huh? The number of ACL entries changed since the last call.
444 Repeat. */
445 }
446
447 if (count == 0)
448 return qset_acl (dst_name, dest_desc, mode);
449
450 ret = (dest_desc != -1
451 ? fsetacl (dest_desc, count, entries)
452 : setacl (dst_name, count, entries));
453 if (ret < 0)
454 {
455 int saved_errno = errno;
456
457 if (errno == ENOSYS || errno == EOPNOTSUPP)
458 {
459 struct stat source_statbuf;
460
461 if ((source_desc != -1
462 ? fstat (source_desc, &source_statbuf)
463 : stat (src_name, &source_statbuf)) == 0)
464 {
465 if (!acl_nontrivial (count, entries, &source_statbuf))
466 return chmod_or_fchmod (dst_name, dest_desc, mode);
467 }
468 else
469 saved_errno = errno;
470 }
471
472 chmod_or_fchmod (dst_name, dest_desc, mode);
473 errno = saved_errno;
474 return -1;
475 }
476
477 if (mode & (S_ISUID | S_ISGID | S_ISVTX))
478 {
479 /* We did not call chmod so far, and either the mode and the ACL are
480 separate or special bits are to be set which don't fit into ACLs. */
481
482 return chmod_or_fchmod (dst_name, dest_desc, mode);
483 }
484 return 0;
485
486 #elif USE_ACL && HAVE_ACLX_GET && 0 /* AIX */
487
488 /* TODO */
489
490 #elif USE_ACL && HAVE_STATACL /* older AIX */
491
492 union { struct acl a; char room[4096]; } u;
493 int ret;
494
495 if ((source_desc != -1
496 ? fstatacl (source_desc, STX_NORMAL, &u.a, sizeof (u))
497 : statacl (src_name, STX_NORMAL, &u.a, sizeof (u)))
498 < 0)
499 return -2;
500
501 ret = (dest_desc != -1
502 ? fchacl (dest_desc, &u.a, u.a.acl_len)
503 : chacl (dst_name, &u.a, u.a.acl_len));
504 if (ret < 0)
505 {
506 int saved_errno = errno;
507
508 chmod_or_fchmod (dst_name, dest_desc, mode);
509 errno = saved_errno;
510 return -1;
511 }
512
513 /* No need to call chmod_or_fchmod at this point, since the mode bits
514 S_ISUID, S_ISGID, S_ISVTX are also stored in the ACL. */
515
516 return 0;
517
518 #else
519
520 return qset_acl (dst_name, dest_desc, mode);
521
522 #endif
523 }
524
525
526 /* Copy access control lists from one file to another. If SOURCE_DESC is
527 a valid file descriptor, use file descriptor operations, else use
528 filename based operations on SRC_NAME. Likewise for DEST_DESC and
529 DST_NAME.
530 If access control lists are not available, fchmod the target file to
531 MODE. Also sets the non-permission bits of the destination file
532 (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
533 Return 0 if successful, otherwise output a diagnostic and return -1. */
534
535 int
copy_acl(const char * src_name,int source_desc,const char * dst_name,int dest_desc,mode_t mode)536 copy_acl (const char *src_name, int source_desc, const char *dst_name,
537 int dest_desc, mode_t mode)
538 {
539 int ret = qcopy_acl (src_name, source_desc, dst_name, dest_desc, mode);
540 switch (ret)
541 {
542 case -2:
543 error (0, errno, "%s", quote (src_name));
544 return -1;
545
546 case -1:
547 error (0, errno, _("preserving permissions for %s"), quote (dst_name));
548 return -1;
549
550 default:
551 return 0;
552 }
553 }
554