• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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