/* Like svn_io_copy_dir_recursively() but doesn't copy regular files that * exist in the destination and do not differ from the source in terms of * kind, size, and mtime. Set *SKIPPED_P to FALSE only if at least one * file was copied, do not change the value in *SKIPPED_P otherwise. * SKIPPED_P may be NULL if not required. */ static svn_error_t * hotcopy_io_copy_dir_recursively(svn_boolean_t *skipped_p, const char *src, const char *dst_parent, const char *dst_basename, svn_boolean_t copy_perms, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool) { svn_node_kind_t kind; apr_status_t status; const char *dst_path; apr_dir_t *this_dir; apr_finfo_t this_entry; apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME; /* Make a subpool for recursion */ apr_pool_t *subpool = svn_pool_create(pool); /* The 'dst_path' is simply dst_parent/dst_basename */ dst_path = svn_dirent_join(dst_parent, dst_basename, pool); /* Sanity checks: SRC and DST_PARENT are directories, and DST_BASENAME doesn't already exist in DST_PARENT. */ SVN_ERR(svn_io_check_path(src, &kind, subpool)); if (kind != svn_node_dir) return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, _("Source '%s' is not a directory"), svn_dirent_local_style(src, pool)); SVN_ERR(svn_io_check_path(dst_parent, &kind, subpool)); if (kind != svn_node_dir) return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, _("Destination '%s' is not a directory"), svn_dirent_local_style(dst_parent, pool)); SVN_ERR(svn_io_check_path(dst_path, &kind, subpool)); /* Create the new directory. */ /* ### TODO: copy permissions (needs apr_file_attrs_get()) */ SVN_ERR(svn_io_make_dir_recursively(dst_path, pool)); /* Loop over the dirents in SRC. ('.' and '..' are auto-excluded) */ SVN_ERR(svn_io_dir_open(&this_dir, src, subpool)); for (status = apr_dir_read(&this_entry, flags, this_dir); status == APR_SUCCESS; status = apr_dir_read(&this_entry, flags, this_dir)) { if ((this_entry.name[0] == '.') && ((this_entry.name[1] == '\0') || ((this_entry.name[1] == '.') && (this_entry.name[2] == '\0')))) { continue; } else { const char *entryname_utf8; if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); SVN_ERR(entry_name_to_utf8(&entryname_utf8, this_entry.name, src, subpool)); if (this_entry.filetype == APR_REG) /* regular file */ { SVN_ERR(hotcopy_io_dir_file_copy(skipped_p, src, dst_path, entryname_utf8, subpool)); } else if (this_entry.filetype == APR_LNK) /* symlink */ { const char *src_target = svn_dirent_join(src, entryname_utf8, subpool); const char *dst_target = svn_dirent_join(dst_path, entryname_utf8, subpool); SVN_ERR(svn_io_copy_link(src_target, dst_target, subpool)); } else if (this_entry.filetype == APR_DIR) /* recurse */ { const char *src_target; /* Prevent infinite recursion by filtering off our newly created destination path. */ if (strcmp(src, dst_parent) == 0 && strcmp(entryname_utf8, dst_basename) == 0) continue; src_target = svn_dirent_join(src, entryname_utf8, subpool); SVN_ERR(hotcopy_io_copy_dir_recursively(skipped_p, src_target, dst_path, entryname_utf8, copy_perms, cancel_func, cancel_baton, subpool)); } /* ### support other APR node types someday?? */ } } if (! (APR_STATUS_IS_ENOENT(status))) return svn_error_wrap_apr(status, _("Can't read directory '%s'"), svn_dirent_local_style(src, pool)); status = apr_dir_close(this_dir); if (status) return svn_error_wrap_apr(status, _("Error closing directory '%s'"), svn_dirent_local_style(src, pool)); /* Free any memory used by recursion */ svn_pool_destroy(subpool); return SVN_NO_ERROR; }
/* Make a copy of the filesystem node (or tree if RECURSIVE) at SRC_ABSPATH under a temporary name in the directory TMPDIR_ABSPATH and return the absolute path of the copy in *DST_ABSPATH. Return the node kind of SRC_ABSPATH in *KIND. If SRC_ABSPATH doesn't exist then set *DST_ABSPATH to NULL to indicate that no copy was made. */ static svn_error_t * copy_to_tmpdir(svn_skel_t **work_item, svn_node_kind_t *kind, svn_wc__db_t *db, const char *src_abspath, const char *dst_abspath, const char *tmpdir_abspath, svn_boolean_t file_copy, svn_boolean_t unversioned, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { svn_boolean_t is_special; svn_io_file_del_t delete_when; const char *dst_tmp_abspath; svn_node_kind_t dsk_kind; if (!kind) kind = &dsk_kind; *work_item = NULL; SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special, scratch_pool)); if (*kind == svn_node_none) { return SVN_NO_ERROR; } else if (*kind == svn_node_unknown) { return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, _("Source '%s' is unexpected kind"), svn_dirent_local_style(src_abspath, scratch_pool)); } else if (*kind == svn_node_dir || is_special) delete_when = svn_io_file_del_on_close; else /* the default case: (*kind == svn_node_file) */ delete_when = svn_io_file_del_none; /* ### Do we need a pool cleanup to remove the copy? We can't use ### svn_io_file_del_on_pool_cleanup above because a) it won't ### handle the directory case and b) we need to be able to remove ### the cleanup before queueing the move work item. */ if (file_copy && !unversioned) { svn_boolean_t modified; /* It's faster to look for mods on the source now, as the timestamp might match, than to examine the destination later as the destination timestamp will never match. */ SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, src_abspath, FALSE, scratch_pool)); if (!modified) { /* Why create a temp copy if we can just reinstall from pristine? */ SVN_ERR(svn_wc__wq_build_file_install(work_item, db, dst_abspath, NULL, FALSE, TRUE, result_pool, scratch_pool)); return SVN_NO_ERROR; } } /* Set DST_TMP_ABSPATH to a temporary unique path. If *KIND is file, leave a file there and then overwrite it; otherwise leave no node on disk at that path. In the latter case, something else might use that path before we get around to using it a moment later, but never mind. */ SVN_ERR(svn_io_open_unique_file3(NULL, &dst_tmp_abspath, tmpdir_abspath, delete_when, scratch_pool, scratch_pool)); if (*kind == svn_node_dir) { if (file_copy) SVN_ERR(svn_io_copy_dir_recursively( src_abspath, tmpdir_abspath, svn_dirent_basename(dst_tmp_abspath, scratch_pool), TRUE, /* copy_perms */ cancel_func, cancel_baton, scratch_pool)); else SVN_ERR(svn_io_dir_make(dst_tmp_abspath, APR_OS_DEFAULT, scratch_pool)); } else if (!is_special) SVN_ERR(svn_io_copy_file(src_abspath, dst_tmp_abspath, TRUE /* copy_perms */, scratch_pool)); else SVN_ERR(svn_io_copy_link(src_abspath, dst_tmp_abspath, scratch_pool)); if (file_copy) { /* Remove 'read-only' from the destination file; it's a local add now. */ SVN_ERR(svn_io_set_file_read_write(dst_tmp_abspath, FALSE, scratch_pool)); } SVN_ERR(svn_wc__wq_build_file_move(work_item, db, dst_abspath, dst_tmp_abspath, dst_abspath, result_pool, scratch_pool)); return SVN_NO_ERROR; }