/* 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; }
/* Copy an un-packed revision or revprop file for revision REV from SRC_SUBDIR * to DST_SUBDIR. Assume a sharding layout based on MAX_FILES_PER_DIR. * Set *SKIPPED_P to FALSE only if the file was copied, do not change the * value in *SKIPPED_P otherwise. SKIPPED_P may be NULL if not required. * Use SCRATCH_POOL for temporary allocations. */ static svn_error_t * hotcopy_copy_shard_file(svn_boolean_t *skipped_p, const char *src_subdir, const char *dst_subdir, svn_revnum_t rev, int max_files_per_dir, apr_pool_t *scratch_pool) { const char *src_subdir_shard = src_subdir, *dst_subdir_shard = dst_subdir; if (max_files_per_dir) { const char *shard = apr_psprintf(scratch_pool, "%ld", rev / max_files_per_dir); src_subdir_shard = svn_dirent_join(src_subdir, shard, scratch_pool); dst_subdir_shard = svn_dirent_join(dst_subdir, shard, scratch_pool); if (rev % max_files_per_dir == 0) { SVN_ERR(svn_io_make_dir_recursively(dst_subdir_shard, scratch_pool)); SVN_ERR(svn_io_copy_perms(dst_subdir, dst_subdir_shard, scratch_pool)); } } SVN_ERR(hotcopy_io_dir_file_copy(skipped_p, src_subdir_shard, dst_subdir_shard, apr_psprintf(scratch_pool, "%ld", rev), scratch_pool)); return SVN_NO_ERROR; }
svn_error_t * svn_wc__ensure_directory(const char *path, apr_pool_t *pool) { svn_node_kind_t kind; SVN_ERR(svn_io_check_path(path, &kind, pool)); if (kind != svn_node_none && kind != svn_node_dir) { /* If got an error other than dir non-existence, then we can't ensure this directory's existence, so just return the error. Might happen if there's a file in the way, for example. */ return svn_error_createf(APR_ENOTDIR, NULL, _("'%s' is not a directory"), svn_dirent_local_style(path, pool)); } else if (kind == svn_node_none) { /* The dir doesn't exist, and it's our job to change that. */ SVN_ERR(svn_io_make_dir_recursively(path, pool)); } else /* No problem, the dir already existed, so just leave. */ SVN_ERR_ASSERT(kind == svn_node_dir); return SVN_NO_ERROR; }
static svn_error_t * dir_add(const char *path, void *parent_baton, const char *copyfrom_path, svn_revnum_t copyfrom_revision, apr_pool_t *result_pool, void **child_baton) { struct dir_baton_t *db; SVN_ERR(dir_open_or_add(path, parent_baton, &db)); if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_revision)) { SVN_ERR(svn_client__repos_to_wc_copy_internal(NULL /*timestamp_sleep*/, svn_node_dir, copyfrom_path, copyfrom_revision, db->local_abspath, db->eb->ra_session, db->eb->ctx, db->pool)); db->created = TRUE; } else { SVN_ERR(svn_io_make_dir_recursively(db->local_abspath, db->pool)); } *child_baton = db; return SVN_NO_ERROR; }
/* svn_delta_editor_t function */ static svn_error_t * edit_open(void *edit_baton, svn_revnum_t base_revision, apr_pool_t *result_pool, void **root_baton) { struct edit_baton_t *eb = edit_baton; apr_pool_t *dir_pool = svn_pool_create(eb->pool); struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db)); db->pool = dir_pool; db->eb = eb; db->users = 1; db->local_abspath = eb->anchor_abspath; SVN_ERR(svn_io_make_dir_recursively(eb->anchor_abspath, dir_pool)); *root_baton = db; return SVN_NO_ERROR; }
dav_error * dav_svn__store_activity(const dav_svn_repos *repos, const char *activity_id, const char *txn_name) { const char *final_path; const char *activity_contents; svn_error_t *err; /* Create activities directory if it does not yet exist. */ err = svn_io_make_dir_recursively(repos->activities_db, repos->pool); if (err != NULL) return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "could not initialize activity db.", repos->pool); final_path = activity_pathname(repos, activity_id); activity_contents = apr_psprintf(repos->pool, "%s\n%s\n", txn_name, activity_id); err = svn_io_write_atomic2(final_path, activity_contents, strlen(activity_contents), NULL /* copy_perms path */, TRUE, repos->pool); if (err) { svn_error_t *serr = svn_error_quick_wrap(err, "Can't write activity db"); /* Try to remove the tmp file, but we already have an error... */ return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "could not write files.", repos->pool); } return NULL; }
svn_error_t * svn_test__create_fake_wc(const char *wc_abspath, const char *extra_statements, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *dotsvn_abspath = svn_dirent_join(wc_abspath, ".svn", scratch_pool); const char *db_abspath = svn_dirent_join(dotsvn_abspath, "wc.db", scratch_pool); svn_sqlite__db_t *sdb; const char **my_statements; int i; /* Allocate MY_STATEMENTS in RESULT_POOL because the SDB will continue to * refer to it over its lifetime. */ my_statements = apr_palloc(result_pool, 6 * sizeof(const char *)); my_statements[0] = statements[STMT_CREATE_SCHEMA]; my_statements[1] = statements[STMT_CREATE_NODES]; my_statements[2] = statements[STMT_CREATE_NODES_TRIGGERS]; my_statements[3] = statements[STMT_CREATE_EXTERNALS]; my_statements[4] = extra_statements; my_statements[5] = NULL; /* Create fake-wc/SUBDIR/.svn/ for placing the metadata. */ SVN_ERR(svn_io_make_dir_recursively(dotsvn_abspath, scratch_pool)); svn_error_clear(svn_io_remove_file2(db_abspath, FALSE, scratch_pool)); SVN_ERR(svn_wc__db_util_open_db(&sdb, wc_abspath, "wc.db", svn_sqlite__mode_rwcreate, FALSE /* exclusive */, my_statements, result_pool, scratch_pool)); for (i = 0; my_statements[i] != NULL; i++) SVN_ERR(svn_sqlite__exec_statements(sdb, /* my_statements[] */ i)); return SVN_NO_ERROR; }
static svn_error_t * dir_add(const char *path, void *parent_baton, const char *copyfrom_path, svn_revnum_t copyfrom_revision, apr_pool_t *result_pool, void **child_baton) { struct dir_baton_t *pb = parent_baton; struct edit_baton_t *eb = pb->eb; apr_pool_t *dir_pool = svn_pool_create(pb->pool); struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db)); svn_boolean_t under_root; pb->users++; db->pb = pb; db->eb = pb->eb; db->pool = dir_pool; db->users = 1; SVN_ERR(svn_dirent_is_under_root(&under_root, &db->local_abspath, eb->anchor_abspath, path, db->pool)); if (! under_root) { return svn_error_createf( SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, _("Path '%s' is not in the working copy"), svn_dirent_local_style(path, db->pool)); } SVN_ERR(svn_io_make_dir_recursively(db->local_abspath, db->pool)); *child_baton = db; return SVN_NO_ERROR; }
/* The main logic of the public svn_client_add4. * * EXISTING_PARENT_ABSPATH is the absolute path to the first existing * parent directory of local_abspath. If not NULL, all missing parents * of LOCAL_ABSPATH must be created before LOCAL_ABSPATH can be added. */ static svn_error_t * add(const char *local_abspath, svn_depth_t depth, svn_boolean_t force, svn_boolean_t no_ignore, const char *existing_parent_abspath, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { svn_node_kind_t kind; svn_error_t *err; svn_magic__cookie_t *magic_cookie; svn_magic__init(&magic_cookie, scratch_pool); if (existing_parent_abspath) { const char *parent_abspath; const char *child_relpath; apr_array_header_t *components; int i; apr_pool_t *iterpool; parent_abspath = existing_parent_abspath; child_relpath = svn_dirent_is_child(existing_parent_abspath, local_abspath, NULL); components = svn_path_decompose(child_relpath, scratch_pool); iterpool = svn_pool_create(scratch_pool); for (i = 0; i < components->nelts - 1; i++) { const char *component; svn_node_kind_t disk_kind; svn_pool_clear(iterpool); if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); component = APR_ARRAY_IDX(components, i, const char *); parent_abspath = svn_dirent_join(parent_abspath, component, scratch_pool); SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, iterpool)); if (disk_kind != svn_node_none && disk_kind != svn_node_dir) return svn_error_createf(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, _("'%s' prevents creating parent of '%s'"), parent_abspath, local_abspath); SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool)); SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, parent_abspath, ctx->notify_func2, ctx->notify_baton2, scratch_pool)); } svn_pool_destroy(iterpool); } SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); if (kind == svn_node_dir) { /* We use add_dir_recursive for all directory targets and pass depth along no matter what it is, so that the target's depth will be set correctly. */ err = add_dir_recursive(local_abspath, depth, force, no_ignore, magic_cookie, ctx, scratch_pool); } else if (kind == svn_node_file) err = add_file(local_abspath, magic_cookie, ctx, scratch_pool); else if (kind == svn_node_none) { svn_boolean_t tree_conflicted; /* Provide a meaningful error message if the node does not exist * on disk but is a tree conflict victim. */ err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted, ctx->wc_ctx, local_abspath, scratch_pool); if (err) svn_error_clear(err); else if (tree_conflicted) return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, _("'%s' is an existing item in conflict; " "please mark the conflict as resolved " "before adding a new item here"), svn_dirent_local_style(local_abspath, scratch_pool)); return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, _("'%s' not found"), svn_dirent_local_style(local_abspath, scratch_pool)); } else return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Unsupported node kind for path '%s'"), svn_dirent_local_style(local_abspath, scratch_pool)); /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set. */ if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) { svn_error_clear(err); err = SVN_NO_ERROR; } return svn_error_trace(err); }
svn_error_t * svn_client__checkout_internal(svn_revnum_t *result_rev, const char *url, const char *local_abspath, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, svn_boolean_t *timestamp_sleep, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_node_kind_t kind; apr_pool_t *session_pool = svn_pool_create(pool); svn_ra_session_t *ra_session; svn_client__pathrev_t *pathrev; /* Sanity check. Without these, the checkout is meaningless. */ SVN_ERR_ASSERT(local_abspath != NULL); SVN_ERR_ASSERT(svn_uri_is_canonical(url, pool)); SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); /* Fulfill the docstring promise of svn_client_checkout: */ if ((revision->kind != svn_opt_revision_number) && (revision->kind != svn_opt_revision_date) && (revision->kind != svn_opt_revision_head)) return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL); /* Get the RA connection. */ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev, url, NULL, peg_revision, revision, ctx, session_pool)); pathrev = svn_client__pathrev_dup(pathrev, pool); SVN_ERR(svn_ra_check_path(ra_session, "", pathrev->rev, &kind, pool)); svn_pool_destroy(session_pool); if (kind == svn_node_none) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' doesn't exist"), pathrev->url); else if (kind == svn_node_file) return svn_error_createf (SVN_ERR_UNSUPPORTED_FEATURE , NULL, _("URL '%s' refers to a file, not a directory"), pathrev->url); SVN_ERR(svn_io_check_path(local_abspath, &kind, pool)); if (kind == svn_node_none) { /* Bootstrap: create an incomplete working-copy root dir. Its entries file should only have an entry for THIS_DIR with a URL, revnum, and an 'incomplete' flag. */ SVN_ERR(svn_io_make_dir_recursively(local_abspath, pool)); SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx, pool)); } else if (kind == svn_node_dir) { int wc_format; const char *entry_url; SVN_ERR(svn_wc_check_wc2(&wc_format, ctx->wc_ctx, local_abspath, pool)); if (! wc_format) { SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx, pool)); } else { /* Get PATH's URL. */ SVN_ERR(svn_wc__node_get_url(&entry_url, ctx->wc_ctx, local_abspath, pool, pool)); /* If PATH's existing URL matches the incoming one, then just update. This allows 'svn co' to restart an interrupted checkout. Otherwise bail out. */ if (strcmp(entry_url, pathrev->url) != 0) return svn_error_createf( SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, _("'%s' is already a working copy for a" " different URL"), svn_dirent_local_style(local_abspath, pool)); } } else { return svn_error_createf(SVN_ERR_WC_NODE_KIND_CHANGE, NULL, _("'%s' already exists and is not a directory"), svn_dirent_local_style(local_abspath, pool)); } /* Have update fix the incompleteness. */ SVN_ERR(svn_client__update_internal(result_rev, local_abspath, revision, depth, TRUE, ignore_externals, allow_unver_obstructions, TRUE /* adds_as_modification */, FALSE, FALSE, timestamp_sleep, ctx, pool)); return SVN_NO_ERROR; }
static svn_error_t * handle_external_item_change(svn_client_ctx_t *ctx, const char *repos_root_url, const char *parent_dir_abspath, const char *parent_dir_url, const char *local_abspath, const char *old_defining_abspath, const svn_wc_external_item2_t *new_item, svn_boolean_t *timestamp_sleep, apr_pool_t *scratch_pool) { svn_ra_session_t *ra_session; svn_client__pathrev_t *new_loc; const char *new_url; svn_node_kind_t ext_kind; SVN_ERR_ASSERT(repos_root_url && parent_dir_url); SVN_ERR_ASSERT(new_item != NULL); /* Don't bother to check status, since we'll get that for free by attempting to retrieve the hash values anyway. */ /* When creating the absolute URL, use the pool and not the iterpool, since the hash table values outlive the iterpool and any pointers they have should also outlive the iterpool. */ SVN_ERR(svn_wc__resolve_relative_external_url(&new_url, new_item, repos_root_url, parent_dir_url, scratch_pool, scratch_pool)); /* Determine if the external is a file or directory. */ /* Get the RA connection. */ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc, new_url, NULL, &(new_item->peg_revision), &(new_item->revision), ctx, scratch_pool)); SVN_ERR(svn_ra_check_path(ra_session, "", new_loc->rev, &ext_kind, scratch_pool)); if (svn_node_none == ext_kind) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' at revision %ld doesn't exist"), new_loc->url, new_loc->rev); if (svn_node_dir != ext_kind && svn_node_file != ext_kind) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' at revision %ld is not a file " "or a directory"), new_loc->url, new_loc->rev); /* Not protecting against recursive externals. Detecting them in the global case is hard, and it should be pretty obvious to a user when it happens. Worst case: your disk fills up :-). */ /* First notify that we're about to handle an external. */ if (ctx->notify_func2) { (*ctx->notify_func2)( ctx->notify_baton2, svn_wc_create_notify(local_abspath, svn_wc_notify_update_external, scratch_pool), scratch_pool); } if (! old_defining_abspath) { /* The target dir might have multiple components. Guarantee the path leading down to the last component. */ SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath, scratch_pool), scratch_pool)); } switch (ext_kind) { case svn_node_dir: SVN_ERR(switch_dir_external(local_abspath, new_loc->url, new_item->url, &(new_item->peg_revision), &(new_item->revision), parent_dir_abspath, timestamp_sleep, ctx, scratch_pool)); break; case svn_node_file: if (strcmp(repos_root_url, new_loc->repos_root_url)) { const char *local_repos_root_url; const char *local_repos_uuid; const char *ext_repos_relpath; svn_error_t *err; /* * The working copy library currently requires that all files * in the working copy have the same repository root URL. * The URL from the file external's definition differs from the * one used by the working copy. As a workaround, replace the * root URL portion of the file external's URL, after making * sure both URLs point to the same repository. See issue #4087. */ err = svn_wc__node_get_repos_info(NULL, NULL, &local_repos_root_url, &local_repos_uuid, ctx->wc_ctx, parent_dir_abspath, scratch_pool, scratch_pool); if (err) { if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) return svn_error_trace(err); svn_error_clear(err); local_repos_root_url = NULL; local_repos_uuid = NULL; } ext_repos_relpath = svn_uri_skip_ancestor(new_loc->repos_root_url, new_url, scratch_pool); if (local_repos_uuid == NULL || local_repos_root_url == NULL || ext_repos_relpath == NULL || strcmp(local_repos_uuid, new_loc->repos_uuid) != 0) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Unsupported external: URL of file external '%s' " "is not in repository '%s'"), new_url, repos_root_url); new_url = svn_path_url_add_component2(local_repos_root_url, ext_repos_relpath, scratch_pool); SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc, new_url, NULL, &(new_item->peg_revision), &(new_item->revision), ctx, scratch_pool)); } SVN_ERR(switch_file_external(local_abspath, new_url, &new_item->peg_revision, &new_item->revision, parent_dir_abspath, ra_session, ctx, scratch_pool)); break; default: SVN_ERR_MALFUNCTION(); break; } return SVN_NO_ERROR; }
/* Try to update a directory external at PATH to URL at REVISION. Use POOL for temporary allocations, and use the client context CTX. */ static svn_error_t * switch_dir_external(const char *local_abspath, const char *url, const char *url_from_externals_definition, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, const char *defining_abspath, svn_boolean_t *timestamp_sleep, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_node_kind_t kind; svn_error_t *err; svn_revnum_t external_peg_rev = SVN_INVALID_REVNUM; svn_revnum_t external_rev = SVN_INVALID_REVNUM; apr_pool_t *subpool = svn_pool_create(pool); const char *repos_root_url; const char *repos_uuid; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); if (peg_revision->kind == svn_opt_revision_number) external_peg_rev = peg_revision->value.number; if (revision->kind == svn_opt_revision_number) external_rev = revision->value.number; /* * The code below assumes existing versioned paths are *not* part of * the external's defining working copy. * The working copy library does not support registering externals * on top of existing BASE nodes and will error out if we try. * So if the external target is part of the defining working copy's * BASE tree, don't attempt to create the external. Doing so would * leave behind a switched path instead of an external (since the * switch succeeds but registration of the external in the DB fails). * The working copy then cannot be updated until the path is switched back. * See issue #4085. */ SVN_ERR(svn_wc__node_get_base(&kind, NULL, NULL, &repos_root_url, &repos_uuid, NULL, ctx->wc_ctx, local_abspath, TRUE, /* ignore_enoent */ TRUE, /* show hidden */ pool, pool)); if (kind != svn_node_unknown) { const char *wcroot_abspath; const char *defining_wcroot_abspath; SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, local_abspath, pool, pool)); SVN_ERR(svn_wc__get_wcroot(&defining_wcroot_abspath, ctx->wc_ctx, defining_abspath, pool, pool)); if (strcmp(wcroot_abspath, defining_wcroot_abspath) == 0) return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, _("The external '%s' defined in %s at '%s' " "cannot be checked out because '%s' is " "already a versioned path."), url_from_externals_definition, SVN_PROP_EXTERNALS, svn_dirent_local_style(defining_abspath, pool), svn_dirent_local_style(local_abspath, pool)); } /* If path is a directory, try to update/switch to the correct URL and revision. */ SVN_ERR(svn_io_check_path(local_abspath, &kind, pool)); if (kind == svn_node_dir) { const char *node_url; /* Doubles as an "is versioned" check. */ err = svn_wc__node_get_url(&node_url, ctx->wc_ctx, local_abspath, pool, subpool); if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) { svn_error_clear(err); err = SVN_NO_ERROR; goto relegate; } else if (err) return svn_error_trace(err); if (node_url) { /* If we have what appears to be a version controlled subdir, and its top-level URL matches that of our externals definition, perform an update. */ if (strcmp(node_url, url) == 0) { SVN_ERR(svn_client__update_internal(NULL, local_abspath, revision, svn_depth_unknown, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, timestamp_sleep, ctx, subpool)); /* We just decided that this existing directory is an external, so update the external registry with this information, like when checking out an external */ SVN_ERR(svn_wc__external_register(ctx->wc_ctx, defining_abspath, local_abspath, svn_node_dir, repos_root_url, repos_uuid, svn_uri_skip_ancestor(repos_root_url, url, pool), external_peg_rev, external_rev, pool)); svn_pool_destroy(subpool); goto cleanup; } /* We'd really prefer not to have to do a brute-force relegation -- blowing away the current external working copy and checking it out anew -- so we'll first see if we can get away with a generally cheaper relocation (if required) and switch-style update. To do so, we need to know the repository root URL of the external working copy as it currently sits. */ err = svn_wc__node_get_repos_info(NULL, NULL, &repos_root_url, &repos_uuid, ctx->wc_ctx, local_abspath, pool, subpool); if (err) { if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) return svn_error_trace(err); svn_error_clear(err); repos_root_url = NULL; repos_uuid = NULL; } if (repos_root_url) { /* If the new external target URL is not obviously a child of the external working copy's current repository root URL... */ if (! svn_uri__is_ancestor(repos_root_url, url)) { const char *repos_root; /* ... then figure out precisely which repository root URL that target URL *is* a child of ... */ SVN_ERR(svn_client_get_repos_root(&repos_root, NULL, url, ctx, subpool, subpool)); /* ... and use that to try to relocate the external working copy to the target location. */ err = svn_client_relocate2(local_abspath, repos_root_url, repos_root, FALSE, ctx, subpool); /* If the relocation failed because the new URL points to a totally different repository, we've no choice but to relegate and check out a new WC. */ if (err && (err->apr_err == SVN_ERR_WC_INVALID_RELOCATION || (err->apr_err == SVN_ERR_CLIENT_INVALID_RELOCATION))) { svn_error_clear(err); goto relegate; } else if (err) return svn_error_trace(err); /* If the relocation went without a hitch, we should have a new repository root URL. */ repos_root_url = repos_root; } SVN_ERR(svn_client__switch_internal(NULL, local_abspath, url, peg_revision, revision, svn_depth_infinity, TRUE, FALSE, FALSE, TRUE /* ignore_ancestry */, timestamp_sleep, ctx, subpool)); SVN_ERR(svn_wc__external_register(ctx->wc_ctx, defining_abspath, local_abspath, svn_node_dir, repos_root_url, repos_uuid, svn_uri_skip_ancestor( repos_root_url, url, subpool), external_peg_rev, external_rev, subpool)); svn_pool_destroy(subpool); goto cleanup; } } } relegate: /* Fall back on removing the WC and checking out a new one. */ /* Ensure that we don't have any RA sessions or WC locks from failed operations above. */ svn_pool_destroy(subpool); if (kind == svn_node_dir) { /* Buh-bye, old and busted ... */ SVN_ERR(relegate_dir_external(ctx->wc_ctx, defining_abspath, local_abspath, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); } else { /* The target dir might have multiple components. Guarantee the path leading down to the last component. */ const char *parent = svn_dirent_dirname(local_abspath, pool); SVN_ERR(svn_io_make_dir_recursively(parent, pool)); } /* ... Hello, new hotness. */ SVN_ERR(svn_client__checkout_internal(NULL, url, local_abspath, peg_revision, revision, svn_depth_infinity, FALSE, FALSE, timestamp_sleep, ctx, pool)); SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL, &repos_root_url, &repos_uuid, ctx->wc_ctx, local_abspath, pool, pool)); SVN_ERR(svn_wc__external_register(ctx->wc_ctx, defining_abspath, local_abspath, svn_node_dir, repos_root_url, repos_uuid, svn_uri_skip_ancestor(repos_root_url, url, pool), external_peg_rev, external_rev, pool)); cleanup: /* Issues #4123 and #4130: We don't need to keep the newly checked out external's DB open. */ SVN_ERR(svn_wc__close_db(local_abspath, ctx->wc_ctx, pool)); return SVN_NO_ERROR; }
svn_error_t * svn_client__export_externals(apr_hash_t *externals, const char *from_url, const char *to_abspath, const char *repos_root_url, svn_depth_t requested_depth, const char *native_eol, svn_boolean_t ignore_keywords, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { apr_pool_t *iterpool = svn_pool_create(scratch_pool); apr_pool_t *sub_iterpool = svn_pool_create(scratch_pool); apr_hash_index_t *hi; SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath)); for (hi = apr_hash_first(scratch_pool, externals); hi; hi = apr_hash_next(hi)) { const char *local_abspath = svn__apr_hash_index_key(hi); const char *desc_text = svn__apr_hash_index_val(hi); const char *local_relpath; const char *dir_url; apr_array_header_t *items; int i; svn_pool_clear(iterpool); SVN_ERR(svn_wc_parse_externals_description3(&items, local_abspath, desc_text, FALSE, iterpool)); if (! items->nelts) continue; local_relpath = svn_dirent_skip_ancestor(to_abspath, local_abspath); dir_url = svn_path_url_add_component2(from_url, local_relpath, scratch_pool); for (i = 0; i < items->nelts; i++) { const char *item_abspath; const char *new_url; svn_boolean_t under_root; svn_wc_external_item2_t *item = APR_ARRAY_IDX(items, i, svn_wc_external_item2_t *); svn_pool_clear(sub_iterpool); SVN_ERR(svn_dirent_is_under_root(&under_root, &item_abspath, local_abspath, item->target_dir, sub_iterpool)); if (! under_root) { return svn_error_createf( SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, _("Path '%s' is not in the working copy"), svn_dirent_local_style( svn_dirent_join(local_abspath, item->target_dir, sub_iterpool), sub_iterpool)); } SVN_ERR(svn_wc__resolve_relative_external_url(&new_url, item, repos_root_url, dir_url, sub_iterpool, sub_iterpool)); /* The target dir might have multiple components. Guarantee the path leading down to the last component. */ SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(item_abspath, sub_iterpool), sub_iterpool)); SVN_ERR(wrap_external_error( ctx, item_abspath, svn_client_export5(NULL, new_url, item_abspath, &item->peg_revision, &item->revision, TRUE, FALSE, ignore_keywords, svn_depth_infinity, native_eol, ctx, sub_iterpool), sub_iterpool)); } } svn_pool_destroy(sub_iterpool); svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
dav_error * dav_svn__store_activity(const dav_svn_repos *repos, const char *activity_id, const char *txn_name) { const char *final_path, *tmp_path, *activity_contents; svn_error_t *err; apr_file_t *activity_file; /* Create activities directory if it does not yet exist. */ err = svn_io_make_dir_recursively(repos->activities_db, repos->pool); if (err != NULL) return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "could not initialize activity db.", repos->pool); final_path = activity_pathname(repos, activity_id); err = svn_io_open_unique_file2(&activity_file, &tmp_path, final_path, ".tmp", svn_io_file_del_none, repos->pool); if (err) { svn_error_t *serr = svn_error_quick_wrap(err, "Can't open activity db"); return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "could not open files.", repos->pool); } activity_contents = apr_psprintf(repos->pool, "%s\n%s\n", txn_name, activity_id); err = svn_io_file_write_full(activity_file, activity_contents, strlen(activity_contents), NULL, repos->pool); if (err) { svn_error_t *serr = svn_error_quick_wrap(err, "Can't write to activity db"); /* Try to remove the tmp file, but we already have an error... */ svn_error_clear(svn_io_file_close(activity_file, repos->pool)); svn_error_clear(svn_io_remove_file(tmp_path, repos->pool)); return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "could not write files.", repos->pool); } err = svn_io_file_close(activity_file, repos->pool); if (err) { svn_error_clear(svn_io_remove_file(tmp_path, repos->pool)); return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "could not close files.", repos->pool); } err = svn_io_file_rename(tmp_path, final_path, repos->pool); if (err) { svn_error_clear(svn_io_remove_file(tmp_path, repos->pool)); return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "could not replace files.", repos->pool); } return NULL; }
/* 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; }
/* 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 an unversioned copy of the versioned file or directory tree at the * source path FROM_ABSPATH. Copy it to the destination path TO_ABSPATH. * * If REVISION is svn_opt_revision_working, copy the working version, * otherwise copy the base version. * * See copy_one_versioned_file() for details of file copying behaviour, * including IGNORE_KEYWORDS and NATIVE_EOL. * * Include externals unless IGNORE_EXTERNALS is true. * * Recurse according to DEPTH. * */ static svn_error_t * copy_versioned_files(const char *from_abspath, const char *to_abspath, const svn_opt_revision_t *revision, svn_boolean_t force, svn_boolean_t ignore_externals, svn_boolean_t ignore_keywords, svn_depth_t depth, const char *native_eol, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_error_t *err; apr_pool_t *iterpool; const apr_array_header_t *children; svn_node_kind_t from_kind; svn_depth_t node_depth; SVN_ERR_ASSERT(svn_dirent_is_absolute(from_abspath)); SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath)); /* Only export 'added' and 'replaced' files when the revision is WORKING; when the revision is BASE (i.e. != WORKING), only export 'added' and 'replaced' files when they are part of a copy-/move-here. Otherwise, skip them, since they don't have an associated text-base. This condition for added/replaced simply is an optimization. Added and replaced files would be handled similarly by svn_wc_get_pristine_contents2(), which would return NULL if they have no base associated. TODO: We may prefer not to duplicate this condition and rather use svn_wc_get_pristine_contents2() or a dedicated new function instead. Don't export 'deleted' files and directories unless it's a revision other than WORKING. These files and directories don't really exist in WORKING. */ if (revision->kind != svn_opt_revision_working) { svn_boolean_t is_added; const char *repos_relpath; SVN_ERR(svn_wc__node_get_origin(&is_added, NULL, &repos_relpath, NULL, NULL, NULL, ctx->wc_ctx, from_abspath, FALSE, pool, pool)); if (is_added && !repos_relpath) return SVN_NO_ERROR; /* Local addition */ } else { svn_boolean_t is_deleted; SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, ctx->wc_ctx, from_abspath, pool)); if (is_deleted) return SVN_NO_ERROR; } SVN_ERR(svn_wc_read_kind(&from_kind, ctx->wc_ctx, from_abspath, FALSE, pool)); if (from_kind == svn_node_dir) { apr_fileperms_t perm = APR_OS_DEFAULT; int j; /* Try to make the new directory. If this fails because the directory already exists, check our FORCE flag to see if we care. */ /* Keep the source directory's permissions if applicable. Skip retrieving the umask on windows. Apr does not implement setting filesystem privileges on Windows. Retrieving the file permissions with APR_FINFO_PROT | APR_FINFO_OWNER is documented to be 'incredibly expensive' */ #ifndef WIN32 if (revision->kind == svn_opt_revision_working) { apr_finfo_t finfo; SVN_ERR(svn_io_stat(&finfo, from_abspath, APR_FINFO_PROT, pool)); perm = finfo.protection; } #endif err = svn_io_dir_make(to_abspath, perm, pool); if (err) { if (! APR_STATUS_IS_EEXIST(err->apr_err)) return svn_error_trace(err); if (! force) SVN_ERR_W(err, _("Destination directory exists, and will not be " "overwritten unless forced")); else svn_error_clear(err); } SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx, from_abspath, FALSE, pool, pool)); iterpool = svn_pool_create(pool); for (j = 0; j < children->nelts; j++) { const char *child_abspath = APR_ARRAY_IDX(children, j, const char *); const char *child_name = svn_dirent_basename(child_abspath, NULL); const char *target_abspath; svn_node_kind_t child_kind; svn_pool_clear(iterpool); if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); target_abspath = svn_dirent_join(to_abspath, child_name, iterpool); SVN_ERR(svn_wc_read_kind(&child_kind, ctx->wc_ctx, child_abspath, FALSE, iterpool)); if (child_kind == svn_node_dir) { if (depth == svn_depth_infinity || depth == svn_depth_immediates) { if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(target_abspath, svn_wc_notify_update_add, pool); notify->kind = svn_node_dir; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } if (depth == svn_depth_infinity) SVN_ERR(copy_versioned_files(child_abspath, target_abspath, revision, force, ignore_externals, ignore_keywords, depth, native_eol, ctx, iterpool)); else SVN_ERR(svn_io_make_dir_recursively(target_abspath, iterpool)); } } else if (child_kind == svn_node_file && depth >= svn_depth_files) { svn_node_kind_t external_kind; SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL, ctx->wc_ctx, child_abspath, child_abspath, TRUE, pool, pool)); if (external_kind != svn_node_file) SVN_ERR(copy_one_versioned_file(child_abspath, target_abspath, ctx, revision, native_eol, ignore_keywords, iterpool)); } } SVN_ERR(svn_wc__node_get_depth(&node_depth, ctx->wc_ctx, from_abspath, pool)); /* Handle externals. */ if (! ignore_externals && depth == svn_depth_infinity && node_depth == svn_depth_infinity) { apr_array_header_t *ext_items; const svn_string_t *prop_val; SVN_ERR(svn_wc_prop_get2(&prop_val, ctx->wc_ctx, from_abspath, SVN_PROP_EXTERNALS, pool, pool)); if (prop_val != NULL) { int i; SVN_ERR(svn_wc_parse_externals_description3(&ext_items, from_abspath, prop_val->data, FALSE, pool)); for (i = 0; i < ext_items->nelts; ++i) { svn_wc_external_item2_t *ext_item; const char *new_from, *new_to; svn_pool_clear(iterpool); ext_item = APR_ARRAY_IDX(ext_items, i, svn_wc_external_item2_t *); new_from = svn_dirent_join(from_abspath, ext_item->target_dir, iterpool); new_to = svn_dirent_join(to_abspath, ext_item->target_dir, iterpool); /* The target dir might have parents that don't exist. Guarantee the path upto the last component. */ if (!svn_dirent_is_root(ext_item->target_dir, strlen(ext_item->target_dir))) { const char *parent = svn_dirent_dirname(new_to, iterpool); SVN_ERR(svn_io_make_dir_recursively(parent, iterpool)); } SVN_ERR(copy_versioned_files(new_from, new_to, revision, force, FALSE, ignore_keywords, svn_depth_infinity, native_eol, ctx, iterpool)); } }
static svn_error_t * test_access_baton_like_locking(apr_pool_t *pool) { svn_wc__db_t *db; svn_wc_context_t *wc_ctx, *wc_ctx2; const char *local_abspath; const char *D, *D1, *D2, *D3, *D4; svn_boolean_t locked_here, locked; svn_error_t *err; svn_wc_adm_access_t *adm_access, *subdir_access; #undef WC_NAME #define WC_NAME "test_access_batons" SVN_ERR(create_open(&db, &local_abspath, WC_NAME, pool)); D = svn_dirent_join(local_abspath, "DD", pool); D1 = svn_dirent_join(D, "DD", pool); D2 = svn_dirent_join(D1, "DD", pool); D3 = svn_dirent_join(D2, "DD", pool); D4 = svn_dirent_join(D3, "DD", pool); SVN_ERR(svn_io_make_dir_recursively(D4, pool)); /* Use the legacy interface */ SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, local_abspath, TRUE, 0, NULL, NULL, pool)); SVN_ERR(svn_wc_add3(D, adm_access, svn_depth_infinity, NULL, SVN_INVALID_REVNUM, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_adm_retrieve(&subdir_access, adm_access, D, pool)); SVN_ERR(svn_wc_add3(D1, subdir_access, svn_depth_infinity, NULL, SVN_INVALID_REVNUM, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_adm_retrieve(&subdir_access, adm_access, D1, pool)); SVN_ERR(svn_wc_add3(D2, subdir_access, svn_depth_infinity, NULL, SVN_INVALID_REVNUM, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_adm_retrieve(&subdir_access, adm_access, D2, pool)); SVN_ERR(svn_wc_add3(D3, subdir_access, svn_depth_infinity, NULL, SVN_INVALID_REVNUM, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_add3(D4, subdir_access, svn_depth_infinity, NULL, SVN_INVALID_REVNUM, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_locked(&locked, D3, pool)); SVN_TEST_ASSERT(locked); SVN_ERR(svn_wc_locked(&locked, D4, pool)); SVN_TEST_ASSERT(locked); SVN_ERR(svn_wc_delete3(D4, subdir_access, NULL, NULL, NULL, NULL, FALSE, pool)); SVN_ERR(svn_wc_locked(&locked, D4, pool)); SVN_TEST_ASSERT(!locked); SVN_ERR(svn_wc_revert3(D, adm_access, svn_depth_infinity, FALSE, NULL, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_locked(&locked, D3, pool)); SVN_TEST_ASSERT(!locked); SVN_ERR(svn_wc_locked(&locked, local_abspath, pool)); SVN_TEST_ASSERT(locked); SVN_ERR(svn_wc_adm_close2(adm_access, pool)); SVN_ERR(svn_wc_context_create(&wc_ctx, NULL, pool, pool)); /* Obtain a lock for the root, which is extended on each level */ SVN_ERR(svn_wc__db_wclock_obtain(wc_ctx->db, local_abspath, 0, FALSE, pool)); SVN_ERR(svn_io_make_dir_recursively(D4, pool)); SVN_ERR(svn_wc_add4(wc_ctx, D, svn_depth_infinity, NULL, SVN_INVALID_REVNUM, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_add4(wc_ctx, D1, svn_depth_infinity, NULL, SVN_INVALID_REVNUM, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_add4(wc_ctx, D2, svn_depth_infinity, NULL, SVN_INVALID_REVNUM, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_add4(wc_ctx, D3, svn_depth_infinity, NULL, SVN_INVALID_REVNUM, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_locked2(&locked_here, &locked, wc_ctx, D3, pool)); SVN_TEST_ASSERT(locked_here && locked); /* Test if the not added path is already locked */ SVN_ERR(svn_wc_locked2(&locked_here, &locked, wc_ctx, D4, pool)); SVN_TEST_ASSERT(!locked_here && !locked); SVN_ERR(svn_wc_add4(wc_ctx, D4, svn_depth_infinity, NULL, SVN_INVALID_REVNUM, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc_locked2(&locked_here, &locked, wc_ctx, D4, pool)); SVN_TEST_ASSERT(locked_here && locked); SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, local_abspath, pool)); /* Should be unlocked */ SVN_ERR(svn_wc_locked2(&locked_here, &locked, wc_ctx, local_abspath, pool)); SVN_TEST_ASSERT(!locked_here && !locked); /* Lock shouldn't be released */ SVN_ERR(svn_wc_locked2(&locked_here, &locked, wc_ctx, D, pool)); SVN_TEST_ASSERT(locked_here && locked); SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, D, pool)); SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, D1, pool)); SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, D2, pool)); SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, D3, pool)); /* Try reobtaining lock on D3; should succeed */ SVN_ERR(svn_wc__db_wclock_obtain(wc_ctx->db, D3, 0, FALSE, pool)); SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, D4, pool)); /* D3 should still be locked; try stealing in a different context */ SVN_ERR(svn_wc_context_create(&wc_ctx2, NULL, pool, pool)); SVN_ERR(svn_wc_locked2(&locked_here, &locked, wc_ctx2, D3, pool)); SVN_TEST_ASSERT(!locked_here && locked); err = svn_wc__db_wclock_obtain(wc_ctx2->db, D3, 0, FALSE, pool); if (err && err->apr_err != SVN_ERR_WC_LOCKED) return svn_error_trace(err); svn_error_clear(err); SVN_TEST_ASSERT(err != NULL); /* Can't lock, as it is still locked */ err = svn_wc__db_wclock_release(wc_ctx2->db, D4, pool); if (err && err->apr_err != SVN_ERR_WC_NOT_LOCKED) return svn_error_trace(err); svn_error_clear(err); SVN_TEST_ASSERT(err != NULL); /* Can't unlock, as it is not ours */ /* Now steal the lock */ SVN_ERR(svn_wc__db_wclock_obtain(wc_ctx2->db, D3, 0, TRUE, pool)); /* We should own the lock now */ SVN_ERR(svn_wc_locked2(&locked_here, &locked, wc_ctx2, D3, pool)); SVN_TEST_ASSERT(locked_here && locked); err = svn_wc__db_wclock_release(wc_ctx2->db, D4, pool); if (err && err->apr_err != SVN_ERR_WC_NOT_LOCKED) return svn_error_trace(err); svn_error_clear(err); SVN_TEST_ASSERT(err != NULL); /* Can't unlock a not locked path */ /* Now create a separate working copy from the same repository directly below this WC and test if our code really sees it as a separate wc, for locking and normal operation */ { const char *url, *repos_root_url, *repos_uuid; const char *subdir = svn_dirent_join(local_abspath, "sub-wc", pool); svn_boolean_t is_root; SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, local_abspath, pool, pool)); SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url, &repos_uuid, wc_ctx, local_abspath, pool, pool)); SVN_ERR(svn_io_make_dir_recursively(subdir, pool)); SVN_ERR(svn_wc_ensure_adm3(subdir, repos_uuid, svn_path_url_add_component2(url, "sub-wc", pool), repos_root_url, 0, svn_depth_infinity, pool)); SVN_ERR(svn_wc__check_wc_root(&is_root, NULL, NULL, wc_ctx->db, subdir, pool)); SVN_TEST_ASSERT(is_root); SVN_ERR(svn_wc__check_wc_root(&is_root, NULL, NULL, wc_ctx2->db, subdir, pool)); /* This test was added to show a regression where the next check failed, but the check above this succeeded */ SVN_TEST_ASSERT(is_root); SVN_ERR(svn_wc_locked2(&locked_here, &locked, wc_ctx2, subdir, pool)); SVN_TEST_ASSERT(!locked_here && !locked); } return SVN_NO_ERROR; }