/* * relocate.c: do wc repos relocation * * ==================================================================== * Copyright (c) 2002-2006 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */ #include "svn_wc.h" #include "svn_error.h" #include "svn_pools.h" #include "svn_path.h" #include "wc.h" #include "entries.h" #include "lock.h" #include "props.h" #include "svn_private_config.h" /* Relocate the main URL and the copyfrom URL for ENTRY by changing FROM to * TO. ADM_ACCESS is the access baton for ENTRY. If DO_SYNC is set then * the new entry will be written to disk immediately, otherwise only the * entries cache will be affected. Calls VALIDATOR passing VALIDATOR_BATON * to validate new URLs. */ static svn_error_t * relocate_entry(svn_wc_adm_access_t *adm_access, const svn_wc_entry_t *entry, const char *from, const char *to, svn_wc_relocation_validator2_t validator, void *validator_baton, svn_boolean_t do_sync, apr_pool_t *pool) { svn_wc_entry_t entry2; apr_uint32_t flags = 0; apr_size_t from_len = strlen(from); if (entry->repos) { /* We can't relocate beyond the repository root, but the user is allowed to specify a redundant part of the fs path in from and to, but only if this part is identical in both strings. */ apr_size_t repos_len = strlen(entry->repos); if (from_len >= repos_len) { apr_size_t to_len = strlen(to); apr_size_t fs_path_len = from_len - repos_len; if (to_len < fs_path_len || strncmp(from + repos_len, to + (to_len - fs_path_len), fs_path_len) != 0) return svn_error_create(SVN_ERR_WC_INVALID_RELOCATION, NULL, _("Relocate can only change the " "repository part of an URL")); /* Since the fs path part is redundant, we don't need to change that part anyway, and the below code depends on this. */ from_len = repos_len; to = apr_pstrndup(pool, to, to_len - fs_path_len); } if (strncmp(from, entry->repos, from_len) == 0) { entry2.repos = apr_pstrcat(pool, to, entry->repos + from_len, NULL); flags |= SVN_WC__ENTRY_MODIFY_REPOS; /* Make sure to is really the repository root. */ SVN_ERR(validator(validator_baton, entry->uuid, entry2.repos, TRUE, pool)); } } if (entry->url && ! strncmp(entry->url, from, from_len)) { entry2.url = apr_pstrcat(pool, to, entry->url + from_len, NULL); if (entry->uuid) SVN_ERR(validator(validator_baton, entry->uuid, entry2.url, FALSE, pool)); flags |= SVN_WC__ENTRY_MODIFY_URL; } if (entry->copyfrom_url && ! strncmp(entry->copyfrom_url, from, from_len)) { entry2.copyfrom_url = apr_pstrcat(pool, to, entry->copyfrom_url + from_len, NULL); if (entry->uuid) SVN_ERR(validator(validator_baton, entry->uuid, entry2.copyfrom_url, FALSE, pool)); flags |= SVN_WC__ENTRY_MODIFY_COPYFROM_URL; } if (flags) SVN_ERR(svn_wc__entry_modify(adm_access, entry->name, &entry2, flags, do_sync, pool)); return SVN_NO_ERROR; } svn_error_t * svn_wc_relocate2(const char *path, svn_wc_adm_access_t *adm_access, const char *from, const char *to, svn_boolean_t recurse, svn_wc_relocation_validator2_t validator, void *validator_baton, apr_pool_t *pool) { apr_hash_t *entries; apr_hash_index_t *hi; const svn_wc_entry_t *entry; apr_pool_t *subpool; SVN_ERR(svn_wc_entry(&entry, path, adm_access, TRUE, pool)); if (! entry) return svn_error_create(SVN_ERR_ENTRY_NOT_FOUND, NULL, NULL); if (entry->kind == svn_node_file) { SVN_ERR(relocate_entry(adm_access, entry, from, to, validator, validator_baton, TRUE /* sync */, pool)); return SVN_NO_ERROR; } /* Relocate THIS_DIR first, in order to pre-validate the relocated URL of all of the other entries. This is technically cheating because it relies on knowledge of the libsvn_client implementation, but it significantly cuts down on the number of expensive validations the validator has to do. ### Should svn_wc.h document the ordering? */ SVN_ERR(svn_wc_entries_read(&entries, adm_access, TRUE, pool)); entry = apr_hash_get(entries, SVN_WC_ENTRY_THIS_DIR, APR_HASH_KEY_STRING); SVN_ERR(relocate_entry(adm_access, entry, from, to, validator, validator_baton, FALSE, pool)); subpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) { const void *key; void *val; apr_hash_this(hi, &key, NULL, &val); entry = val; if (strcmp(key, SVN_WC_ENTRY_THIS_DIR) == 0) continue; svn_pool_clear(subpool); if (recurse && (entry->kind == svn_node_dir) && (! entry->deleted || (entry->schedule == svn_wc_schedule_add)) && ! entry->absent) { svn_wc_adm_access_t *subdir_access; const char *subdir = svn_path_join(path, key, subpool); if (svn_wc__adm_missing(adm_access, subdir)) continue; SVN_ERR(svn_wc_adm_retrieve(&subdir_access, adm_access, subdir, subpool)); SVN_ERR(svn_wc_relocate2(subdir, subdir_access, from, to, recurse, validator, validator_baton, subpool)); } SVN_ERR(relocate_entry(adm_access, entry, from, to, validator, validator_baton, FALSE, subpool)); } svn_pool_destroy(subpool); SVN_ERR(svn_wc__remove_wcprops(adm_access, NULL, FALSE, pool)); SVN_ERR(svn_wc__entries_write(entries, adm_access, pool)); return SVN_NO_ERROR; } /* Compatibility baton and wrapper. */ struct compat_baton { svn_wc_relocation_validator_t validator; void *baton; }; /* This implements svn_wc_relocate_validator2_t. */ static svn_error_t * compat_validator(void *baton, const char *uuid, const char *url, svn_boolean_t root, apr_pool_t *pool) { struct compat_baton *cb = baton; /* The old callback type doesn't allow uuid to be NULL. */ if (uuid) return cb->validator(cb->baton, uuid, url); return SVN_NO_ERROR; } svn_error_t * svn_wc_relocate(const char *path, svn_wc_adm_access_t *adm_access, const char *from, const char *to, svn_boolean_t recurse, svn_wc_relocation_validator_t validator, void *validator_baton, apr_pool_t *pool) { struct compat_baton cb; cb.validator = validator; cb.baton = validator_baton; return svn_wc_relocate2(path, adm_access, from, to, recurse, compat_validator, &cb, pool); }