/* Create an empty repository and WC for the test TEST_NAME. Set *REPOS_URL * to the URL of the new repository and *WC_ABSPATH to the root path of the * new WC. * * Create the repository and WC in subdirectories called * REPOSITORIES_WORK_DIR/TEST_NAME and WCS_WORK_DIR/TEST_NAME respectively, * within the current working directory. * * Register the repo and WC to be cleaned up when the test suite exits. */ static svn_error_t * create_repos_and_wc(const char **repos_url, const char **wc_abspath, const char *test_name, const svn_test_opts_t *opts, apr_pool_t *pool) { const char *repos_path = svn_relpath_join(REPOSITORIES_WORK_DIR, test_name, pool); const char *wc_path = svn_relpath_join(WCS_WORK_DIR, test_name, pool); /* Remove the repo and WC dirs if they already exist, to ensure the test * will run even if a previous failed attempt was not cleaned up. */ SVN_ERR(svn_io_remove_dir2(repos_path, TRUE, NULL, NULL, pool)); SVN_ERR(svn_io_remove_dir2(wc_path, TRUE, NULL, NULL, pool)); /* Create the parent dirs of the repo and WC if necessary. */ SVN_ERR(svn_io_make_dir_recursively(REPOSITORIES_WORK_DIR, pool)); SVN_ERR(svn_io_make_dir_recursively(WCS_WORK_DIR, pool)); /* Create a repos. Register it for clean-up. Set *REPOS_URL to its path. */ { svn_repos_t *repos; /* Use a subpool to create the repository and then destroy the subpool so the repository's underlying filesystem is closed. If opts->fs_type is BDB this prevents any attempt to open a second environment handle within the same process when we checkout the WC below. BDB 4.4+ allows only a single environment handle to be open per process. */ apr_pool_t *subpool = svn_pool_create(pool); SVN_ERR(svn_test__create_repos(&repos, repos_path, opts, subpool)); SVN_ERR(svn_uri_get_file_url_from_dirent(repos_url, repos_path, pool)); svn_pool_destroy(subpool); } /* Create a WC. Set *WC_ABSPATH to its path. */ { apr_pool_t *subpool = svn_pool_create(pool); /* To cleanup CTX */ svn_client_ctx_t *ctx; svn_opt_revision_t head_rev = { svn_opt_revision_head, {0} }; SVN_ERR(svn_client_create_context(&ctx, subpool)); SVN_ERR(svn_dirent_get_absolute(wc_abspath, wc_path, pool)); SVN_ERR(svn_client_checkout3(NULL, *repos_url, *wc_abspath, &head_rev, &head_rev, svn_depth_infinity, FALSE /* ignore_externals */, FALSE /* allow_unver_obstructions */, ctx, subpool)); svn_pool_destroy(subpool); } /* Register this WC for cleanup. */ svn_test_add_dir_cleanup(*wc_abspath); return SVN_NO_ERROR; }
/* Remove/erase PATH from the working copy. This involves deleting PATH * from the physical filesystem. PATH is assumed to be an unversioned file * or directory. * * If ignore_enoent is TRUE, ignore missing targets. * * If CANCEL_FUNC is non-null, invoke it with CANCEL_BATON at various * points, return any error immediately. */ static svn_error_t * erase_unversioned_from_wc(const char *path, svn_boolean_t ignore_enoent, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { svn_error_t *err; /* Optimize the common case: try to delete the file */ err = svn_io_remove_file2(path, ignore_enoent, scratch_pool); if (err) { /* Then maybe it was a directory? */ svn_error_clear(err); err = svn_io_remove_dir2(path, ignore_enoent, cancel_func, cancel_baton, scratch_pool); if (err) { /* We're unlikely to end up here. But we need this fallback to make sure we report the right error *and* try the correct deletion at least once. */ svn_node_kind_t kind; svn_error_clear(err); SVN_ERR(svn_io_check_path(path, &kind, scratch_pool)); if (kind == svn_node_file) SVN_ERR(svn_io_remove_file2(path, ignore_enoent, scratch_pool)); else if (kind == svn_node_dir) SVN_ERR(svn_io_remove_dir2(path, ignore_enoent, cancel_func, cancel_baton, scratch_pool)); else if (kind == svn_node_none) return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL, _("'%s' does not exist"), svn_dirent_local_style(path, scratch_pool)); else return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Unsupported node kind for path '%s'"), svn_dirent_local_style(path, scratch_pool)); } } return SVN_NO_ERROR; }
/* Delete the filesystem located at path PATH. Perform any temporary allocations in POOL. */ static svn_error_t * fs_delete_fs(const char *path, apr_pool_t *pool) { /* Remove everything. */ return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); }
svn_error_t * svn_wc__adm_destroy(svn_wc__db_t *db, const char *dir_abspath, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { const char *adm_abspath; SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath)); SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool)); SVN_ERR(svn_wc__db_get_wcroot(&adm_abspath, db, dir_abspath, scratch_pool, scratch_pool)); /* Well, the coast is clear for blowing away the administrative directory, which also removes remaining locks */ /* Now close the DB, and we can delete the working copy */ if (strcmp(adm_abspath, dir_abspath) == 0) { SVN_ERR(svn_wc__db_drop_root(db, adm_abspath, scratch_pool)); SVN_ERR(svn_io_remove_dir2(svn_wc__adm_child(adm_abspath, NULL, scratch_pool), FALSE, cancel_func, cancel_baton, scratch_pool)); } return SVN_NO_ERROR; }
/* Delete the filesystem located at path PATH. Perform any temporary allocations in SCRATCH_POOL. */ static svn_error_t * x_delete_fs(const char *path, apr_pool_t *scratch_pool) { /* Remove everything. */ return svn_error_trace(svn_io_remove_dir2(path, FALSE, NULL, NULL, scratch_pool)); }
static svn_error_t * base_delete_fs(const char *path, apr_pool_t *pool) { /* First, use the Berkeley DB library function to remove any shared memory segments. */ SVN_ERR(svn_fs_bdb__remove(path, pool)); /* Remove the environment directory. */ return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); }
svn_error_t * svn_wc__adm_destroy(svn_wc_adm_access_t *adm_access, apr_pool_t *scratch_pool) { const char *path; SVN_ERR(svn_wc__adm_write_check(adm_access, scratch_pool)); /* Well, the coast is clear for blowing away the administrative directory, which also removes the lock file */ path = svn_wc__adm_child(svn_wc_adm_access_path(adm_access), NULL, scratch_pool); SVN_ERR(svn_io_remove_dir2(path, FALSE, NULL, NULL, scratch_pool)); return svn_wc_adm_close2(adm_access, scratch_pool); }
/* Remove folder PATH. Ignore errors due to the sub-tree not being empty. * CANCEL_FUNC and CANCEL_BATON do the usual thing. * Use POOL for temporary allocations. */ static svn_error_t * remove_folder(const char *path, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool) { svn_error_t *err = svn_io_remove_dir2(path, TRUE, cancel_func, cancel_baton, pool); if (err && APR_STATUS_IS_ENOTEMPTY(err->apr_err)) { svn_error_clear(err); err = SVN_NO_ERROR; } return svn_error_trace(err); }
static svn_error_t * create_fake_wc(const char *subdir, apr_pool_t *pool) { const char *root; const char *wc_abspath; root = svn_dirent_join("fake-wc", subdir, pool); SVN_ERR(svn_io_remove_dir2(root, TRUE, NULL, NULL, pool)); SVN_ERR(svn_dirent_get_absolute(&wc_abspath, root, pool)); SVN_ERR(svn_test__create_fake_wc(wc_abspath, TESTING_DATA, pool, pool)); wc_abspath = svn_dirent_join(wc_abspath, "M", pool); SVN_ERR(svn_test__create_fake_wc(wc_abspath, M_TESTING_DATA, pool, pool)); return SVN_NO_ERROR; }
svn_error_t * svn_wc__adm_cleanup_tmp_area(svn_wc__db_t *db, const char *adm_abspath, apr_pool_t *scratch_pool) { const char *tmp_path; SVN_ERR_ASSERT(svn_dirent_is_absolute(adm_abspath)); SVN_ERR(svn_wc__write_check(db, adm_abspath, scratch_pool)); /* Get the path to the tmp area, and blow it away. */ tmp_path = svn_wc__adm_child(adm_abspath, SVN_WC__ADM_TMP, scratch_pool); SVN_ERR(svn_io_remove_dir2(tmp_path, TRUE, NULL, NULL, scratch_pool)); /* Now, rebuild the tmp area. */ return svn_error_trace(init_adm_tmp_area(adm_abspath, scratch_pool)); }
svn_error_t * svn_wc__adm_cleanup_tmp_area(const svn_wc_adm_access_t *adm_access, apr_pool_t *scratch_pool) { const char *tmp_path; /* If the admin area doesn't even *exist*, then the temp area is definitely cleaned up. */ if (!svn_wc__adm_area_exists(adm_access, scratch_pool)) return SVN_NO_ERROR; SVN_ERR(svn_wc__adm_write_check(adm_access, scratch_pool)); /* Get the path to the tmp area, and blow it away. */ tmp_path = svn_wc__adm_child(svn_wc_adm_access_path(adm_access), SVN_WC__ADM_TMP, scratch_pool); SVN_ERR(svn_io_remove_dir2(tmp_path, TRUE, NULL, NULL, scratch_pool)); /* Now, rebuild the tmp area. */ return init_adm_tmp_area(adm_access, scratch_pool); }
static apr_status_t cleanup_rmtree(void *data) { if (!skip_cleanup) { apr_pool_t *pool = svn_pool_create(NULL); const char *path = data; /* Ignore errors here. */ svn_error_t *err = svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); svn_error_clear(err); if (verbose_mode) { if (err) printf("FAILED CLEANUP: %s\n", path); else printf("CLEANUP: %s\n", path); } svn_pool_destroy(pool); } return APR_SUCCESS; }
/* Perform a hotcopy, either normal or incremental. * * Normal hotcopy assumes that the destination exists as an empty * directory. It behaves like an incremental hotcopy except that * none of the copied files already exist in the destination. * * An incremental hotcopy copies only changed or new files to the destination, * and removes files from the destination no longer present in the source. * While the incremental hotcopy is running, readers should still be able * to access the destintation repository without error and should not see * revisions currently in progress of being copied. Readers are able to see * new fully copied revisions even if the entire incremental hotcopy procedure * has not yet completed. * * Writers are blocked out completely during the entire incremental hotcopy * process to ensure consistency. This function assumes that the repository * write-lock is held. */ static svn_error_t * hotcopy_body(void *baton, apr_pool_t *pool) { struct hotcopy_body_baton *hbb = baton; svn_fs_t *src_fs = hbb->src_fs; fs_fs_data_t *src_ffd = src_fs->fsap_data; svn_fs_t *dst_fs = hbb->dst_fs; fs_fs_data_t *dst_ffd = dst_fs->fsap_data; svn_boolean_t incremental = hbb->incremental; svn_fs_hotcopy_notify_t notify_func = hbb->notify_func; void* notify_baton = hbb->notify_baton; svn_cancel_func_t cancel_func = hbb->cancel_func; void* cancel_baton = hbb->cancel_baton; svn_revnum_t src_youngest; apr_uint64_t src_next_node_id; apr_uint64_t src_next_copy_id; svn_revnum_t dst_youngest; const char *src_revprops_dir; const char *dst_revprops_dir; const char *src_revs_dir; const char *dst_revs_dir; const char *src_subdir; const char *dst_subdir; svn_node_kind_t kind; /* Try to copy the config. * * ### We try copying the config file before doing anything else, * ### because higher layers will abort the hotcopy if we throw * ### an error from this function, and that renders the hotcopy * ### unusable anyway. */ if (src_ffd->format >= SVN_FS_FS__MIN_CONFIG_FILE) { svn_error_t *err; err = svn_io_dir_file_copy(src_fs->path, dst_fs->path, PATH_CONFIG, pool); if (err) { if (APR_STATUS_IS_ENOENT(err->apr_err)) { /* 1.6.0 to 1.6.11 did not copy the configuration file during * hotcopy. So if we're hotcopying a repository which has been * created as a hotcopy itself, it's possible that fsfs.conf * does not exist. Ask the user to re-create it. * * ### It would be nice to make this a non-fatal error, * ### but this function does not get an svn_fs_t object * ### so we have no way of just printing a warning via * ### the fs->warning() callback. */ const char *src_abspath; const char *dst_abspath; const char *config_relpath; svn_error_t *err2; config_relpath = svn_dirent_join(src_fs->path, PATH_CONFIG, pool); err2 = svn_dirent_get_absolute(&src_abspath, src_fs->path, pool); if (err2) return svn_error_trace(svn_error_compose_create(err, err2)); err2 = svn_dirent_get_absolute(&dst_abspath, dst_fs->path, pool); if (err2) return svn_error_trace(svn_error_compose_create(err, err2)); /* ### hack: strip off the 'db/' directory from paths so * ### they make sense to the user */ src_abspath = svn_dirent_dirname(src_abspath, pool); dst_abspath = svn_dirent_dirname(dst_abspath, pool); return svn_error_quick_wrapf(err, _("Failed to create hotcopy at '%s'. " "The file '%s' is missing from the source " "repository. Please create this file, for " "instance by running 'svnadmin upgrade %s'"), dst_abspath, config_relpath, src_abspath); } else return svn_error_trace(err); } } if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); /* Find the youngest revision in the source and destination. * We only support hotcopies from sources with an equal or greater amount * of revisions than the destination. * This also catches the case where users accidentally swap the * source and destination arguments. */ SVN_ERR(svn_fs_fs__read_current(&src_youngest, &src_next_node_id, &src_next_copy_id, src_fs, pool)); if (incremental) { SVN_ERR(svn_fs_fs__youngest_rev(&dst_youngest, dst_fs, pool)); if (src_youngest < dst_youngest) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("The hotcopy destination already contains more revisions " "(%lu) than the hotcopy source contains (%lu); are source " "and destination swapped?"), dst_youngest, src_youngest); } else dst_youngest = 0; src_revs_dir = svn_dirent_join(src_fs->path, PATH_REVS_DIR, pool); dst_revs_dir = svn_dirent_join(dst_fs->path, PATH_REVS_DIR, pool); src_revprops_dir = svn_dirent_join(src_fs->path, PATH_REVPROPS_DIR, pool); dst_revprops_dir = svn_dirent_join(dst_fs->path, PATH_REVPROPS_DIR, pool); /* Ensure that the required folders exist in the destination * before actually copying the revisions and revprops. */ SVN_ERR(svn_io_make_dir_recursively(dst_revs_dir, pool)); SVN_ERR(svn_io_make_dir_recursively(dst_revprops_dir, pool)); if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); /* Split the logic for new and old FS formats. The latter is much simpler * due to the absense of sharding and packing. However, it requires special * care when updating the 'current' file (which contains not just the * revision number, but also the next-ID counters). */ if (src_ffd->format >= SVN_FS_FS__MIN_NO_GLOBAL_IDS_FORMAT) { SVN_ERR(hotcopy_revisions(src_fs, dst_fs, src_youngest, dst_youngest, incremental, src_revs_dir, dst_revs_dir, src_revprops_dir, dst_revprops_dir, notify_func, notify_baton, cancel_func, cancel_baton, pool)); SVN_ERR(svn_fs_fs__write_current(dst_fs, src_youngest, 0, 0, pool)); } else { SVN_ERR(hotcopy_revisions_old(src_fs, dst_fs, src_youngest, src_revs_dir, dst_revs_dir, src_revprops_dir, dst_revprops_dir, notify_func, notify_baton, cancel_func, cancel_baton, pool)); SVN_ERR(svn_fs_fs__write_current(dst_fs, src_youngest, src_next_node_id, src_next_copy_id, pool)); } /* Replace the locks tree. * This is racy in case readers are currently trying to list locks in * the destination. However, we need to get rid of stale locks. * This is the simplest way of doing this, so we accept this small race. */ dst_subdir = svn_dirent_join(dst_fs->path, PATH_LOCKS_DIR, pool); SVN_ERR(svn_io_remove_dir2(dst_subdir, TRUE, cancel_func, cancel_baton, pool)); src_subdir = svn_dirent_join(src_fs->path, PATH_LOCKS_DIR, pool); SVN_ERR(svn_io_check_path(src_subdir, &kind, pool)); if (kind == svn_node_dir) SVN_ERR(svn_io_copy_dir_recursively(src_subdir, dst_fs->path, PATH_LOCKS_DIR, TRUE, cancel_func, cancel_baton, pool)); /* Now copy the node-origins cache tree. */ src_subdir = svn_dirent_join(src_fs->path, PATH_NODE_ORIGINS_DIR, pool); SVN_ERR(svn_io_check_path(src_subdir, &kind, pool)); if (kind == svn_node_dir) SVN_ERR(hotcopy_io_copy_dir_recursively(NULL, src_subdir, dst_fs->path, PATH_NODE_ORIGINS_DIR, TRUE, cancel_func, cancel_baton, pool)); /* * NB: Data copied below is only read by writers, not readers. * Writers are still locked out at this point. */ if (dst_ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT) { /* Copy the rep cache and then remove entries for revisions * that did not make it into the destination. */ src_subdir = svn_dirent_join(src_fs->path, REP_CACHE_DB_NAME, pool); dst_subdir = svn_dirent_join(dst_fs->path, REP_CACHE_DB_NAME, pool); SVN_ERR(svn_io_check_path(src_subdir, &kind, pool)); if (kind == svn_node_file) { SVN_ERR(svn_sqlite__hotcopy(src_subdir, dst_subdir, pool)); /* The source might have r/o flags set on it - which would be carried over to the copy. */ SVN_ERR(svn_io_set_file_read_write(dst_subdir, FALSE, pool)); SVN_ERR(svn_fs_fs__del_rep_reference(dst_fs, src_youngest, pool)); } } /* Copy the txn-current file. */ if (dst_ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT) SVN_ERR(svn_io_dir_file_copy(src_fs->path, dst_fs->path, PATH_TXN_CURRENT, pool)); return SVN_NO_ERROR; }