/* Copy the permissions of src_path to dst_path. This includes the file mode permission bits and ACLs. File ownership is not copied. */ int perm_copy_fd (const char *src_path, int src_fd, const char *dst_path, int dst_fd, struct error_context *ctx) { #if defined(HAVE_ACL_GET_FD) && defined(HAVE_ACL_SET_FD) acl_t acl; #endif struct stat st; int ret = 0; ret = fstat(src_fd, &st); if (ret != 0) { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); return -1; } #if defined(HAVE_ACL_GET_FD) && defined(HAVE_ACL_SET_FD) /* POSIX 1003.1e draft 17 (abandoned) specific version. */ acl = acl_get_fd (src_fd); if (acl == NULL) { ret = -1; if (errno == ENOSYS || errno == ENOTSUP) ret = set_acl_fd (dst_path, dst_fd, st.st_mode, ctx); else { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); } return ret; } if (acl_set_fd (dst_fd, acl) != 0) { int saved_errno = errno; __apply_mask_to_mode(&st.st_mode, acl); ret = fchmod (dst_fd, st.st_mode); if ((errno != ENOSYS && errno != ENOTSUP) || acl_entries (acl) != 3) { const char *qpath = quote (ctx, dst_path); errno = saved_errno; error (ctx, _("preserving permissions for %s"), qpath); quote_free (ctx, qpath); ret = -1; } } (void) acl_free (acl); return ret; #else /* POSIX.1 version. */ ret = fchmod (dst_fd, st.st_mode); if (ret != 0) { const char *qpath = quote (ctx, dst_path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } return ret; #endif }
/* Set the access control list of path to the permissions defined by mode. */ static int set_acl (char const *path, mode_t mode, struct error_context *ctx) { int ret = 0; #if defined(HAVE_ACL_FROM_MODE) && defined(HAVE_ACL_SET_FILE) /* POSIX 1003.1e draft 17 (abandoned) specific version. */ acl_t acl = acl_from_mode (mode); if (!acl) { error (ctx, ""); return -1; } if (acl_set_file (path, ACL_TYPE_ACCESS, acl) != 0) { ret = -1; if (errno == ENOTSUP || errno == ENOSYS) { (void) acl_free (acl); goto chmod_only; } else { const char *qpath = quote (ctx, path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } } (void) acl_free (acl); if (ret == 0 && S_ISDIR (mode)) { # if defined(HAVE_ACL_DELETE_DEF_FILE) ret = acl_delete_def_file (path); # else acl = acl_init (0); ret = acl_set_file (path, ACL_TYPE_DEFAULT, acl); (void) acl_free (acl); # endif if (ret != 0) { const char *qpath = quote (ctx, path); error (ctx, _( "setting permissions for %s"), qpath); quote_free (ctx, qpath); } } return ret; #endif chmod_only: ret = chmod (path, mode); if (ret != 0) { const char *qpath = quote (ctx, path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } return ret; }
/* Set the access control list of path to the permissions defined by mode. */ static int set_acl_fd (char const *path, int fd, mode_t mode, struct error_context *ctx) { int ret = 0; #if defined(HAVE_ACL_FROM_MODE) && defined(HAVE_ACL_SET_FD) /* POSIX 1003.1e draft 17 (abandoned) specific version. */ acl_t acl = acl_from_mode (mode); if (!acl) { error (ctx, ""); return -1; } if (acl_set_fd (fd, acl) != 0) { ret = -1; if (errno == ENOTSUP || errno == ENOSYS) { (void) acl_free (acl); goto chmod_only; } else { const char *qpath = quote (ctx, path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } } (void) acl_free (acl); return ret; #endif chmod_only: ret = fchmod (fd, mode); if (ret != 0) { const char *qpath = quote (ctx, path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } return ret; }
/* Copy the permissions of src_path to dst_path. This includes the file mode permission bits and ACLs. File ownership is not copied. */ int perm_copy_file (const char *src_path, const char *dst_path, struct error_context *ctx) { #if defined(HAVE_ACL_GET_FILE) && defined(HAVE_ACL_SET_FILE) acl_t acl; #endif struct stat st; int ret = 0; ret = stat(src_path, &st); if (ret != 0) { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); return -1; } #if defined(HAVE_ACL_GET_FILE) && defined(HAVE_ACL_SET_FILE) /* POSIX 1003.1e draft 17 (abandoned) specific version. */ acl = acl_get_file (src_path, ACL_TYPE_ACCESS); if (acl == NULL) { ret = -1; if (errno == ENOSYS || errno == ENOTSUP) ret = set_acl (dst_path, st.st_mode, ctx); else { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); } return ret; } if (acl_set_file (dst_path, ACL_TYPE_ACCESS, acl) != 0) { int saved_errno = errno; __apply_mask_to_mode(&st.st_mode, acl); ret = chmod (dst_path, st.st_mode); if ((errno != ENOSYS && errno != ENOTSUP) || acl_entries (acl) != 3) { const char *qpath = quote (ctx, dst_path); errno = saved_errno; error (ctx, _("preserving permissions for %s"), qpath); quote_free (ctx, qpath); ret = -1; } } (void) acl_free (acl); if (ret == 0 && S_ISDIR (st.st_mode)) { acl = acl_get_file (src_path, ACL_TYPE_DEFAULT); if (acl == NULL) { const char *qpath = quote (ctx, src_path); error (ctx, "%s", qpath); quote_free (ctx, qpath); return -1; } # if defined(HAVE_ACL_DELETE_DEF_FILE) if (acl_entries(acl) == 0) ret = acl_delete_def_file(dst_path); else ret = acl_set_file (dst_path, ACL_TYPE_DEFAULT, acl); # else ret = acl_set_file (dst_path, ACL_TYPE_DEFAULT, acl); # endif if (ret != 0) { const char *qpath = quote (ctx, dst_path); error (ctx, _("preserving permissions for %s"), qpath); quote_free (ctx, qpath); } (void) acl_free(acl); } return ret; #else /* POSIX.1 version. */ ret = chmod (dst_path, st.st_mode); if (ret != 0) { const char *qpath = quote (ctx, dst_path); error (ctx, _("setting permissions for %s"), qpath); quote_free (ctx, qpath); } return ret; #endif }
/* Copy extended attributes from src_path to dst_path. If the file has an extended Access ACL (system.posix_acl_access) and that is copied successfully, the file mode permission bits are copied as a side effect. This may not always the case, so the file mode and/or ownership must be copied separately. */ int attr_copy_fd(const char *src_path, int src_fd, const char *dst_path, int dst_fd, int (*check) (const char *, struct error_context *), struct error_context *ctx) { #if defined(HAVE_FLISTXATTR) && defined(HAVE_FGETXATTR) && \ defined(HAVE_FSETXATTR) int ret = 0; ssize_t size; char *names = NULL, *end_names, *name, *value = NULL; unsigned int setxattr_ENOTSUP = 0; /* ignore acls by default */ if (check == NULL) check = attr_copy_check_permissions; size = flistxattr (src_fd, NULL, 0); if (size < 0) { if (errno != ENOSYS && errno != ENOTSUP) { const char *qpath = quote (ctx, src_path); error (ctx, _("listing attributes of %s"), qpath); quote_free (ctx, qpath); ret = -1; } goto getout; } names = (char *) my_alloc (size+1); if (names == NULL) { error (ctx, ""); ret = -1; goto getout; } size = flistxattr (src_fd, names, size); if (size < 0) { const char *qpath = quote (ctx, src_path); error (ctx, _("listing attributes of %s"), qpath); quote_free (ctx, qpath); ret = -1; goto getout; } else { names[size] = '\0'; end_names = names + size; } for (name = names; name != end_names; name = strchr(name, '\0') + 1) { void *old_value; /* check if this attribute shall be preserved */ if (!*name || !check(name, ctx)) continue; size = fgetxattr (src_fd, name, NULL, 0); if (size < 0) { const char *qpath = quote (ctx, src_path); const char *qname = quote (ctx, name); error (ctx, _("getting attribute %s of %s"), qpath, qname); quote_free (ctx, qname); quote_free (ctx, qpath); ret = -1; continue; } value = (char *) realloc (old_value = value, size); if (size != 0 && value == NULL) { free(old_value); error (ctx, ""); ret = -1; } size = fgetxattr (src_fd, name, value, size); if (size < 0) { const char *qpath = quote (ctx, src_path); const char *qname = quote (ctx, name); error (ctx, _("getting attribute %s of %s"), qname, qpath); quote_free (ctx, qname); quote_free (ctx, qpath); ret = -1; continue; } if (fsetxattr (dst_fd, name, value, size, 0) != 0) { if (errno == ENOTSUP) setxattr_ENOTSUP++; else { const char *qpath = quote (ctx, dst_path); if (errno == ENOSYS) { error (ctx, _("setting attributes for " "%s"), qpath); ret = -1; break; /* no hope of getting any further */ } else { const char *qname = quote (ctx, name); error (ctx, _("setting attribute %s for %s"), qname, qpath); quote_free (ctx, qname); ret = -1; } quote_free (ctx, qpath); } } } if (setxattr_ENOTSUP) { const char *qpath = quote (ctx, dst_path); errno = ENOTSUP; error (ctx, _("setting attributes for %s"), qpath); ret = -1; quote_free (ctx, qpath); } getout: free (value); my_free (names); return ret; #else return 0; #endif }