(root)/
acl-2.3.1/
libacl/
perm_copy_file.c
       1  /* Copy POSIX 1003.1e draft 17 (abandoned) ACLs between files. */
       2   
       3  /* Copyright (C) 2002 Andreas Gruenbacher <agruen@suse.de>, SuSE Linux AG.
       4  
       5    This program is free software; you can redistribute it and/or
       6    modify it under the terms of the GNU Lesser General Public
       7    License as published by the Free Software Foundation; either
       8    version 2.1 of the License, or (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 GNU
      13    Lesser General Public License for more details.
      14  
      15    You should have received a copy of the GNU Lesser General Public
      16    License along with this library; if not, write to the Free Software
      17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
      18  */
      19  
      20  #if defined (HAVE_CONFIG_H)
      21  #include "config.h"
      22  #endif
      23  #if defined(HAVE_LIBACL_LIBACL_H)
      24  # include "libacl.h"
      25  #endif
      26  
      27  #include <sys/types.h>
      28  #include <sys/stat.h>
      29  #include <unistd.h>
      30  #include <stdlib.h>
      31  #include <errno.h>
      32  
      33  #if defined(HAVE_SYS_ACL_H)
      34  #include <sys/acl.h>
      35  #endif
      36  
      37  #if defined(HAVE_ACL_LIBACL_H)
      38  #include <acl/libacl.h>
      39  #endif
      40  
      41  #define ERROR_CONTEXT_MACROS
      42  #ifdef HAVE_ATTR_ERROR_CONTEXT_H
      43  #include <attr/error_context.h>
      44  #else
      45  #include "error_context.h"
      46  #endif
      47  
      48  #if !defined(ENOTSUP)
      49  # define ENOTSUP (-1)
      50  #endif
      51  
      52  #if !defined(HAVE_ACL_FREE)
      53  static int
      54  acl_free(void *obj_p)
      55  {
      56  	free (obj_p);
      57  	return 0;
      58  }
      59  #endif
      60  
      61  #if !defined(HAVE_ACL_ENTRIES)
      62  static int
      63  acl_entries(acl_t acl)
      64  {
      65  # if defined(HAVE_ACL_GET_ENTRY)
      66  	/* POSIX 1003.1e draft 17 (abandoned) compatible version.  */
      67  	acl_entry_t entry;
      68  	int entries = 0;
      69  
      70  	int entries = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
      71  	if (entries > 0) {
      72  		while (acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) > 0)
      73  			entries++;
      74  	}
      75  	return entries;
      76  # else
      77  	return -1;
      78  # endif
      79  }
      80  #endif
      81  
      82  #if !defined(HAVE_ACL_FROM_MODE) && defined(HAVE_ACL_FROM_TEXT)
      83  # define HAVE_ACL_FROM_MODE
      84  static acl_t
      85  acl_from_mode(mode_t mode)
      86  {
      87  	char acl_text[] = "u::---,g::---,o::---";
      88  	acl_t acl;
      89  
      90  	if (mode & S_IRUSR) acl_text[ 3] = 'r';
      91  	if (mode & S_IWUSR) acl_text[ 4] = 'w';
      92  	if (mode & S_IXUSR) acl_text[ 5] = 'x';
      93  	if (mode & S_IRGRP) acl_text[10] = 'r';
      94  	if (mode & S_IWGRP) acl_text[11] = 'w';
      95  	if (mode & S_IXGRP) acl_text[12] = 'x';
      96  	if (mode & S_IROTH) acl_text[17] = 'r';
      97  	if (mode & S_IWOTH) acl_text[18] = 'w';
      98  	if (mode & S_IXOTH) acl_text[19] = 'x';
      99  
     100  	return acl_from_text (acl_text);
     101  }
     102  #endif
     103  
     104  /* Set the access control list of path to the permissions defined by mode.  */
     105  static int
     106  set_acl (char const *path, mode_t mode, struct error_context *ctx)
     107  {
     108  	int ret = 0;
     109  #if defined(HAVE_ACL_FROM_MODE) && defined(HAVE_ACL_SET_FILE)
     110  	/* POSIX 1003.1e draft 17 (abandoned) specific version.  */
     111  	acl_t acl = acl_from_mode (mode);
     112  	if (!acl) {
     113  		error (ctx, "");
     114  		return -1;
     115  	}
     116  
     117  	if (acl_set_file (path, ACL_TYPE_ACCESS, acl) != 0) {
     118  		ret = -1;
     119  		if (errno == ENOTSUP || errno == ENOSYS) {
     120  			(void) acl_free (acl);
     121  			goto chmod_only;
     122  		} else {
     123  			const char *qpath = quote (ctx, path);
     124  			error (ctx, _("setting permissions for %s"), qpath);
     125  			quote_free (ctx, qpath);
     126  		}
     127  	}
     128  	(void) acl_free (acl);
     129  	if (ret == 0 && S_ISDIR (mode)) {
     130  # if defined(HAVE_ACL_DELETE_DEF_FILE)
     131  		ret = acl_delete_def_file (path);
     132  # else
     133  		acl = acl_init (0);
     134  		ret = acl_set_file (path, ACL_TYPE_DEFAULT, acl);
     135  		(void) acl_free (acl);
     136  # endif
     137  		if (ret != 0) {
     138  			const char *qpath = quote (ctx, path);
     139  			error (ctx, _( "setting permissions for %s"), qpath);
     140  			quote_free (ctx, qpath);
     141  		}
     142  	}
     143  	return ret;
     144  #endif
     145  
     146  chmod_only:
     147  	ret = chmod (path, mode);
     148  	if (ret != 0) {
     149  		const char *qpath = quote (ctx, path);
     150  		error (ctx, _("setting permissions for %s"), qpath);
     151  		quote_free (ctx, qpath);
     152  	}
     153  	return ret;
     154  }
     155  
     156  /* Copy the permissions of src_path to dst_path. This includes the
     157     file mode permission bits and ACLs. File ownership is not copied.
     158   */
     159  int
     160  perm_copy_file (const char *src_path, const char *dst_path,
     161  		 struct error_context *ctx)
     162  {
     163  #if defined(HAVE_ACL_GET_FILE) && defined(HAVE_ACL_SET_FILE)
     164  	acl_t acl;
     165  #endif
     166  	struct stat st;
     167  	int ret = 0;
     168  
     169  	ret = stat(src_path, &st);
     170  	if (ret != 0) {
     171  		const char *qpath = quote (ctx, src_path);
     172  		error (ctx, "%s", qpath);
     173  		quote_free (ctx, qpath);
     174  		return -1;
     175  	}
     176  #if defined(HAVE_ACL_GET_FILE) && defined(HAVE_ACL_SET_FILE)
     177  	/* POSIX 1003.1e draft 17 (abandoned) specific version.  */
     178  	acl = acl_get_file (src_path, ACL_TYPE_ACCESS);
     179  	if (acl == NULL) {
     180  		ret = -1;
     181  		if (errno == ENOSYS || errno == ENOTSUP)
     182  			ret = set_acl (dst_path, st.st_mode, ctx);
     183  		else {
     184  			const char *qpath = quote (ctx, src_path);
     185  			error (ctx, "%s", qpath);
     186  			quote_free (ctx, qpath);
     187  		}
     188  		return ret;
     189  	}
     190  
     191  	if (acl_set_file (dst_path, ACL_TYPE_ACCESS, acl) != 0) {
     192  		int saved_errno = errno;
     193  		__apply_mask_to_mode(&st.st_mode, acl);
     194  		ret = chmod (dst_path, st.st_mode);
     195  		if ((errno != ENOSYS && errno != ENOTSUP) ||
     196  		    acl_entries (acl) != 3) {
     197  			const char *qpath = quote (ctx, dst_path);
     198  			errno = saved_errno;
     199  			error (ctx, _("preserving permissions for %s"), qpath);
     200  			quote_free (ctx, qpath);
     201  			ret = -1;
     202  		}
     203  	}
     204  	(void) acl_free (acl);
     205  
     206  	if (ret == 0 && S_ISDIR (st.st_mode)) {
     207  		acl = acl_get_file (src_path, ACL_TYPE_DEFAULT);
     208  		if (acl == NULL) {
     209  			const char *qpath = quote (ctx, src_path);
     210  			error (ctx, "%s", qpath);
     211  			quote_free (ctx, qpath);
     212  			return -1;
     213  		}
     214  # if defined(HAVE_ACL_DELETE_DEF_FILE)
     215  		if (acl_entries(acl) == 0)
     216  			ret = acl_delete_def_file(dst_path);
     217  		else
     218  			ret = acl_set_file (dst_path, ACL_TYPE_DEFAULT, acl);
     219  # else
     220  		ret = acl_set_file (dst_path, ACL_TYPE_DEFAULT, acl);
     221  # endif
     222  		if (ret != 0) {
     223  			const char *qpath = quote (ctx, dst_path);
     224  			error (ctx, _("preserving permissions for %s"), qpath);
     225  			quote_free (ctx, qpath);
     226  		}
     227  		(void) acl_free(acl);
     228  	}
     229  	return ret;
     230  #else
     231  	/* POSIX.1 version. */
     232  	ret = chmod (dst_path, st.st_mode);
     233  	if (ret != 0) {
     234  		const char *qpath = quote (ctx, dst_path);
     235  		error (ctx, _("setting permissions for %s"), qpath);
     236  		quote_free (ctx, qpath);
     237  	}
     238  	return ret;
     239  #endif
     240  }
     241