static svn_error_t * test_boolean_retrieval(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_config_t *cfg; int i; const char *cfg_file; SVN_ERR(get_config_file_path(&cfg_file, opts, pool)); SVN_ERR(svn_config_read3(&cfg, cfg_file, TRUE, FALSE, FALSE, pool)); for (i = 0; true_keys[i] != NULL; i++) { svn_boolean_t value; SVN_ERR(svn_config_get_bool(cfg, &value, "booleans", true_keys[i], FALSE)); if (!value) return fail(pool, "Value of option '%s' is not true", true_keys[i]); } for (i = 0; false_keys[i] != NULL; i++) { svn_boolean_t value; SVN_ERR(svn_config_get_bool(cfg, &value, "booleans", false_keys[i], TRUE)); if (value) return fail(pool, "Value of option '%s' is not true", false_keys[i]); } { svn_error_t *err; svn_boolean_t value; svn_error_clear((err = svn_config_get_bool(cfg, &value, "booleans", "bad_true", TRUE))); if (!err) return fail(pool, "No error on bad truth value"); svn_error_clear((err = svn_config_get_bool(cfg, &value, "booleans", "bad_false", FALSE))); if (!err) return fail(pool, "No error on bad truth value"); } { svn_boolean_t value; SVN_ERR(svn_config_get_server_setting_bool(cfg, &value, "server group", "setting", FALSE)); if (value) return svn_error_create(SVN_ERR_TEST_FAILED, SVN_NO_ERROR, "Expected a svn_config_get_server_setting_bool()" "to return FALSE, but it returned TRUE"); } return SVN_NO_ERROR; }
static const char * get_application_name(apr_hash_t *parameters, apr_pool_t *pool) { svn_config_t *config = static_cast<svn_config_t *> (apr_hash_get(parameters, SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING)); svn_boolean_t svn_application_name_with_pid; svn_config_get_bool(config, &svn_application_name_with_pid, SVN_CONFIG_SECTION_AUTH, SVN_CONFIG_OPTION_KWALLET_SVN_APPLICATION_NAME_WITH_PID, FALSE); const char *svn_application_name; if (svn_application_name_with_pid) { svn_application_name = apr_psprintf(pool, "Subversion [%ld]", long(getpid())); } else { svn_application_name = "Subversion"; } return svn_application_name; }
svn_error_t * svn_magic__init(svn_magic__cookie_t **magic_cookie, apr_hash_t *config, apr_pool_t *result_pool) { svn_magic__cookie_t *mc = NULL; #ifdef SVN_HAVE_LIBMAGIC if (config) { svn_boolean_t enable; svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG); SVN_ERR(svn_config_get_bool(cfg, &enable, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_ENABLE_MAGIC_FILE, TRUE)); if (!enable) { *magic_cookie = NULL; return SVN_NO_ERROR; } } mc = apr_palloc(result_pool, sizeof(*mc)); /* Initialise libmagic. */ #ifndef MAGIC_MIME_TYPE /* Some old versions of libmagic don't support MAGIC_MIME_TYPE. * We can use MAGIC_MIME instead. It returns more than we need * but we can work around that (see below). */ mc->magic = magic_open(MAGIC_MIME | MAGIC_ERROR); #else mc->magic = magic_open(MAGIC_MIME_TYPE | MAGIC_ERROR); #endif if (mc->magic) { /* This loads the default magic database. * Point the MAGIC environment variable at your favourite .mgc * file to load a non-default database. */ if (magic_load(mc->magic, NULL) == -1) { magic_close(mc->magic); mc = NULL; } else apr_pool_cleanup_register(result_pool, mc, close_magic_cookie, apr_pool_cleanup_null); } #endif *magic_cookie = mc; return SVN_NO_ERROR; }
/* Return a memcache in *MEMCACHE_P for FS if it's configured to use memcached, or NULL otherwise. Also, sets *FAIL_STOP to a boolean indicating whether cache errors should be returned to the caller or just passed to the FS warning handler. Use FS->pool for allocating the memcache, and POOL for temporary allocations. */ static svn_error_t * read_config(svn_memcache_t **memcache_p, svn_boolean_t *fail_stop, svn_fs_t *fs, apr_pool_t *pool) { fs_fs_data_t *ffd = fs->fsap_data; SVN_ERR(svn_cache__make_memcache_from_config(memcache_p, ffd->config, fs->pool)); return svn_config_get_bool(ffd->config, fail_stop, CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP, FALSE); }
svn_error_t * svn_wc__db_open(svn_wc__db_t **db, svn_config_t *config, svn_boolean_t open_without_upgrade, svn_boolean_t enforce_empty_wq, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { *db = apr_pcalloc(result_pool, sizeof(**db)); (*db)->config = config; (*db)->verify_format = !open_without_upgrade; (*db)->enforce_empty_wq = enforce_empty_wq; (*db)->dir_data = apr_hash_make(result_pool); (*db)->state_pool = result_pool; /* Don't need to initialize (*db)->parse_cache, due to the calloc above */ if (config) { svn_error_t *err; svn_boolean_t sqlite_exclusive = FALSE; apr_int64_t timeout; err = svn_config_get_bool(config, &sqlite_exclusive, SVN_CONFIG_SECTION_WORKING_COPY, SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE, FALSE); if (err) { svn_error_clear(err); } else (*db)->exclusive = sqlite_exclusive; err = svn_config_get_int64(config, &timeout, SVN_CONFIG_SECTION_WORKING_COPY, SVN_CONFIG_OPTION_SQLITE_BUSY_TIMEOUT, 0); if (err || timeout < 0 || timeout > APR_INT32_MAX) svn_error_clear(err); else (*db)->timeout = (apr_int32_t)timeout; } return SVN_NO_ERROR; }
/* Return a memcache in *MEMCACHE_P for FS if it's configured to use memcached, or NULL otherwise. Also, sets *FAIL_STOP to a boolean indicating whether cache errors should be returned to the caller or just passed to the FS warning handler. Use FS->pool for allocating the memcache, and POOL for temporary allocations. */ static svn_error_t * read_config(svn_memcache_t **memcache_p, svn_boolean_t *fail_stop, svn_boolean_t *cache_txdeltas, svn_boolean_t *cache_fulltexts, svn_fs_t *fs, apr_pool_t *pool) { fs_fs_data_t *ffd = fs->fsap_data; SVN_ERR(svn_cache__make_memcache_from_config(memcache_p, ffd->config, fs->pool)); /* don't cache text deltas by default. * Once we reconstructed the fulltexts from the deltas, * these deltas are rarely re-used. Therefore, only tools * like svnadmin will activate this to speed up operations * dump and verify. */ *cache_txdeltas = svn_hash__get_bool(fs->config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, FALSE); /* by default, cache fulltexts. * Most SVN tools care about reconstructed file content. * Thus, this is a reasonable default. * SVN admin tools may set that to FALSE because fulltexts * won't be re-used rendering the cache less effective * by squeezing wanted data out. */ *cache_fulltexts = svn_hash__get_bool(fs->config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, TRUE); return svn_config_get_bool(ffd->config, fail_stop, CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP, FALSE); }
svn_error_t *svn_ra_open4(svn_ra_session_t **session_p, const char **corrected_url_p, const char *repos_URL, const char *uuid, const svn_ra_callbacks2_t *callbacks, void *callback_baton, apr_hash_t *config, apr_pool_t *pool) { apr_pool_t *sesspool = svn_pool_create(pool); svn_ra_session_t *session; const struct ra_lib_defn *defn; const svn_ra__vtable_t *vtable = NULL; svn_config_t *servers = NULL; const char *server_group; apr_uri_t repos_URI; apr_status_t apr_err; #ifdef CHOOSABLE_DAV_MODULE const char *http_library = DEFAULT_HTTP_LIBRARY; #endif /* Auth caching parameters. */ svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS; svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS; const char *store_plaintext_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS; svn_boolean_t store_pp = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP; const char *store_pp_plaintext = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT; const char *corrected_url; /* Initialize the return variable. */ *session_p = NULL; apr_err = apr_uri_parse(sesspool, repos_URL, &repos_URI); /* ### Should apr_uri_parse leave hostname NULL? It doesn't * for "file:///" URLs, only for bogus URLs like "bogus". * If this is the right behavior for apr_uri_parse, maybe we * should have a svn_uri_parse wrapper. */ if (apr_err != APR_SUCCESS || repos_URI.hostname == NULL) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Illegal repository URL '%s'"), repos_URL); if (callbacks->auth_baton) { /* The 'store-passwords' and 'store-auth-creds' parameters used to * live in SVN_CONFIG_CATEGORY_CONFIG. For backward compatibility, * if values for these parameters have already been set by our * callers, we use those values as defaults. * * Note that we can only catch the case where users explicitly set * "store-passwords = no" or 'store-auth-creds = no". * * However, since the default value for both these options is * currently (and has always been) "yes", users won't know * the difference if they set "store-passwords = yes" or * "store-auth-creds = yes" -- they'll get the expected behaviour. */ if (svn_auth_get_parameter(callbacks->auth_baton, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL) store_passwords = FALSE; if (svn_auth_get_parameter(callbacks->auth_baton, SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL) store_auth_creds = FALSE; } if (config) { /* Grab the 'servers' config. */ servers = apr_hash_get(config, SVN_CONFIG_CATEGORY_SERVERS, APR_HASH_KEY_STRING); if (servers) { /* First, look in the global section. */ SVN_ERR(svn_config_get_bool (servers, &store_passwords, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_STORE_PASSWORDS, store_passwords)); SVN_ERR(svn_config_get_yes_no_ask (servers, &store_plaintext_passwords, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS)); SVN_ERR(svn_config_get_bool (servers, &store_pp, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, store_pp)); SVN_ERR(svn_config_get_yes_no_ask (servers, &store_pp_plaintext, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT)); SVN_ERR(svn_config_get_bool (servers, &store_auth_creds, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_STORE_AUTH_CREDS, store_auth_creds)); /* Find out where we're about to connect to, and * try to pick a server group based on the destination. */ server_group = svn_config_find_group(servers, repos_URI.hostname, SVN_CONFIG_SECTION_GROUPS, sesspool); if (server_group) { /* Override global auth caching parameters with the ones * for the server group, if any. */ SVN_ERR(svn_config_get_bool(servers, &store_auth_creds, server_group, SVN_CONFIG_OPTION_STORE_AUTH_CREDS, store_auth_creds)); SVN_ERR(svn_config_get_bool(servers, &store_passwords, server_group, SVN_CONFIG_OPTION_STORE_PASSWORDS, store_passwords)); SVN_ERR(svn_config_get_yes_no_ask (servers, &store_plaintext_passwords, server_group, SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, store_plaintext_passwords)); SVN_ERR(svn_config_get_bool (servers, &store_pp, server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, store_pp)); SVN_ERR(svn_config_get_yes_no_ask (servers, &store_pp_plaintext, server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, store_pp_plaintext)); } #ifdef CHOOSABLE_DAV_MODULE /* Now, which DAV-based RA method do we want to use today? */ http_library = svn_config_get_server_setting(servers, server_group, /* NULL is OK */ SVN_CONFIG_OPTION_HTTP_LIBRARY, DEFAULT_HTTP_LIBRARY); if (strcmp(http_library, "neon") != 0 && strcmp(http_library, "serf") != 0) return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, _("Invalid config: unknown HTTP library " "'%s'"), http_library); #endif } } if (callbacks->auth_baton) { /* Save auth caching parameters in the auth parameter hash. */ if (! store_passwords) svn_auth_set_parameter(callbacks->auth_baton, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, ""); svn_auth_set_parameter(callbacks->auth_baton, SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS, store_plaintext_passwords); if (! store_pp) svn_auth_set_parameter(callbacks->auth_baton, SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP, ""); svn_auth_set_parameter(callbacks->auth_baton, SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, store_pp_plaintext); if (! store_auth_creds) svn_auth_set_parameter(callbacks->auth_baton, SVN_AUTH_PARAM_NO_AUTH_CACHE, ""); } /* Find the library. */ for (defn = ra_libraries; defn->ra_name != NULL; ++defn) { const char *scheme; if ((scheme = has_scheme_of(defn->schemes, repos_URL))) { svn_ra__init_func_t initfunc = defn->initfunc; #ifdef CHOOSABLE_DAV_MODULE if (defn->schemes == dav_schemes && strcmp(defn->ra_name, http_library) != 0) continue; #endif if (! initfunc) SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name, sesspool)); if (! initfunc) /* Library not found. */ continue; SVN_ERR(initfunc(svn_ra_version(), &vtable, sesspool)); SVN_ERR(check_ra_version(vtable->get_version(), scheme)); if (! has_scheme_of(vtable->get_schemes(sesspool), repos_URL)) /* Library doesn't support the scheme at runtime. */ continue; break; } } if (vtable == NULL) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Unrecognized URL scheme for '%s'"), repos_URL); /* Create the session object. */ session = apr_pcalloc(sesspool, sizeof(*session)); session->vtable = vtable; session->pool = sesspool; /* Ask the library to open the session. */ SVN_ERR_W(vtable->open_session(session, &corrected_url, repos_URL, callbacks, callback_baton, config, sesspool), apr_psprintf(pool, "Unable to connect to a repository at URL '%s'", repos_URL)); /* If the session open stuff detected a server-provided URL correction (a 301 or 302 redirect response during the initial OPTIONS request), then kill the session so the caller can decide what to do. */ if (corrected_url_p && corrected_url) { if (! svn_path_is_url(corrected_url)) { /* RFC1945 and RFC2616 state that the Location header's value (from whence this CORRECTED_URL ultimately comes), if present, must be an absolute URI. But some Apache versions (those older than 2.2.11, it seems) transmit only the path portion of the URI. See issue #3775 for details. */ apr_uri_t corrected_URI = repos_URI; corrected_URI.path = (char *)corrected_url; corrected_url = apr_uri_unparse(pool, &corrected_URI, 0); } *corrected_url_p = svn_uri_canonicalize(corrected_url, pool); svn_pool_destroy(sesspool); return SVN_NO_ERROR; } /* Check the UUID. */ if (uuid) { const char *repository_uuid; SVN_ERR(vtable->get_uuid(session, &repository_uuid, pool)); if (strcmp(uuid, repository_uuid) != 0) { /* Duplicate the uuid as it is allocated in sesspool */ repository_uuid = apr_pstrdup(pool, repository_uuid); svn_pool_destroy(sesspool); return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL, _("Repository UUID '%s' doesn't match " "expected UUID '%s'"), repository_uuid, uuid); } } *session_p = session; return SVN_NO_ERROR; }
/* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__help(apr_getopt_t *os, void *baton, apr_pool_t *pool) { svn_cl__opt_state_t *opt_state = NULL; svn_stringbuf_t *version_footer = NULL; /* xgettext: the %s is for SVN_VER_NUMBER. */ char help_header_template[] = N_("usage: svn <subcommand> [options] [args]\n" "Subversion command-line client, version %s.\n" "Type 'svn help <subcommand>' for help on a specific subcommand.\n" "Type 'svn --version' to see the program version and RA modules\n" " or 'svn --version --quiet' to see just the version number.\n" "\n" "Most subcommands take file and/or directory arguments, recursing\n" "on the directories. If no arguments are supplied to such a\n" "command, it recurses on the current directory (inclusive) by default.\n" "\n" "Available subcommands:\n"); char help_footer[] = N_("Subversion is a tool for version control.\n" "For additional information, see http://subversion.apache.org/\n"); char *help_header = apr_psprintf(pool, _(help_header_template), SVN_VER_NUMBER); const char *ra_desc_start = _("The following repository access (RA) modules are available:\n\n"); if (baton) { svn_cl__cmd_baton_t *const cmd_baton = baton; #ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE /* Windows never actually stores plaintext passwords, it encrypts the contents using CryptoAPI. ... ... If CryptoAPI is available ... but it should be on all versions of Windows that are even remotely interesting two days before the scheduled end of the world, when this comment is being written. */ # ifndef WIN32 svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS; svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS; svn_boolean_t store_plaintext_passwords = FALSE; svn_config_t *cfg; if (cmd_baton->ctx->config) { cfg = svn_hash_gets(cmd_baton->ctx->config, SVN_CONFIG_CATEGORY_CONFIG); if (cfg) { SVN_ERR(svn_config_get_bool(cfg, &store_auth_creds, SVN_CONFIG_SECTION_AUTH, SVN_CONFIG_OPTION_STORE_AUTH_CREDS, store_auth_creds)); SVN_ERR(svn_config_get_bool(cfg, &store_passwords, SVN_CONFIG_SECTION_AUTH, SVN_CONFIG_OPTION_STORE_PASSWORDS, store_passwords)); } cfg = svn_hash_gets(cmd_baton->ctx->config, SVN_CONFIG_CATEGORY_SERVERS); if (cfg) { const char *value; SVN_ERR(svn_config_get_yes_no_ask (cfg, &value, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS)); if (0 == svn_cstring_casecmp(value, SVN_CONFIG_TRUE)) store_plaintext_passwords = TRUE; } } if (store_plaintext_passwords && store_auth_creds && store_passwords) { version_footer = svn_stringbuf_create( _("WARNING: Plaintext password storage is enabled!\n\n"), pool); svn_stringbuf_appendcstr(version_footer, ra_desc_start); } # endif /* !WIN32 */ #endif /* !SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE */ opt_state = cmd_baton->opt_state; } if (!version_footer) version_footer = svn_stringbuf_create(ra_desc_start, pool); SVN_ERR(svn_ra_print_modules(version_footer, pool)); return svn_opt_print_help4(os, "svn", /* ### erm, derive somehow? */ opt_state ? opt_state->version : FALSE, opt_state ? opt_state->quiet : FALSE, opt_state ? opt_state->verbose : FALSE, version_footer->data, help_header, /* already gettext()'d */ svn_cl__cmd_table, svn_cl__options, svn_cl__global_options, _(help_footer), pool); }
svn_error_t * svn_client_revert2(const apr_array_header_t *paths, svn_depth_t depth, const apr_array_header_t *changelists, svn_client_ctx_t *ctx, apr_pool_t *pool) { apr_pool_t *subpool; svn_error_t *err = SVN_NO_ERROR; int i; svn_config_t *cfg; svn_boolean_t use_commit_times; struct revert_with_write_lock_baton baton; /* Don't even attempt to modify the working copy if any of the * targets look like URLs. URLs are invalid input. */ for (i = 0; i < paths->nelts; i++) { const char *path = APR_ARRAY_IDX(paths, i, const char *); if (svn_path_is_url(path)) return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, _("'%s' is not a local path"), path); } cfg = ctx->config ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); subpool = svn_pool_create(pool); for (i = 0; i < paths->nelts; i++) { const char *path = APR_ARRAY_IDX(paths, i, const char *); const char *local_abspath, *lock_target; svn_boolean_t wc_root; svn_pool_clear(subpool); /* See if we've been asked to cancel this operation. */ if ((ctx->cancel_func) && ((err = ctx->cancel_func(ctx->cancel_baton)))) goto errorful; err = svn_dirent_get_absolute(&local_abspath, path, pool); if (err) goto errorful; baton.local_abspath = local_abspath; baton.depth = depth; baton.use_commit_times = use_commit_times; baton.changelists = changelists; baton.ctx = ctx; err = svn_wc__is_wcroot(&wc_root, ctx->wc_ctx, local_abspath, pool); if (err) goto errorful; lock_target = wc_root ? local_abspath : svn_dirent_dirname(local_abspath, pool); err = svn_wc__call_with_write_lock(revert, &baton, ctx->wc_ctx, lock_target, FALSE, pool, pool); if (err) goto errorful; } errorful: { /* Sleep to ensure timestamp integrity. */ const char *sleep_path = NULL; /* Only specify a path if we are certain all paths are on the same filesystem */ if (paths->nelts == 1) sleep_path = APR_ARRAY_IDX(paths, 0, const char *); svn_io_sleep_for_timestamps(sleep_path, subpool); } svn_pool_destroy(subpool); return svn_error_trace(err); }
svn_error_t * svn_client__get_auto_props(apr_hash_t **properties, const char **mimetype, const char *path, svn_magic__cookie_t *magic_cookie, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_config_t *cfg; svn_boolean_t use_autoprops; auto_props_baton_t autoprops; /* initialisation */ autoprops.properties = apr_hash_make(pool); autoprops.filename = svn_dirent_basename(path, pool); autoprops.pool = pool; autoprops.mimetype = NULL; autoprops.have_executable = FALSE; *properties = autoprops.properties; cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING) : NULL; /* check that auto props is enabled */ SVN_ERR(svn_config_get_bool(cfg, &use_autoprops, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE)); /* search for auto props */ if (use_autoprops) svn_config_enumerate2(cfg, SVN_CONFIG_SECTION_AUTO_PROPS, auto_props_enumerator, &autoprops, pool); /* if mimetype has not been set check the file */ if (! autoprops.mimetype) { SVN_ERR(svn_io_detect_mimetype2(&autoprops.mimetype, path, ctx->mimetypes_map, pool)); /* If we got no mime-type, or if it is "application/octet-stream", * try to get the mime-type from libmagic. */ if (magic_cookie && (!autoprops.mimetype || strcmp(autoprops.mimetype, "application/octet-stream") == 0)) { const char *magic_mimetype; /* Since libmagic usually treats UTF-16 files as "text/plain", * svn_magic__detect_binary_mimetype() will return NULL for such * files. This is fine for now since we currently don't support * UTF-16-encoded text files (issue #2194). * Once we do support UTF-16 this code path will fail to detect * them as text unless the svn_io_detect_mimetype2() call above * returns "text/plain" for them. */ SVN_ERR(svn_magic__detect_binary_mimetype(&magic_mimetype, path, magic_cookie, pool, pool)); if (magic_mimetype) autoprops.mimetype = magic_mimetype; } if (autoprops.mimetype) apr_hash_set(autoprops.properties, SVN_PROP_MIME_TYPE, strlen(SVN_PROP_MIME_TYPE), svn_string_create(autoprops.mimetype, pool)); } /* if executable has not been set check the file */ if (! autoprops.have_executable) { svn_boolean_t executable = FALSE; SVN_ERR(svn_io_is_file_executable(&executable, path, pool)); if (executable) apr_hash_set(autoprops.properties, SVN_PROP_EXECUTABLE, strlen(SVN_PROP_EXECUTABLE), svn_string_create_empty(pool)); } *mimetype = autoprops.mimetype; return SVN_NO_ERROR; }
/* Try to update a file external at LOCAL_ABSPATH to URL at REVISION using a access baton that has a write lock. Use SCRATCH_POOL for temporary allocations, and use the client context CTX. */ static svn_error_t * switch_file_external(const char *local_abspath, const char *url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, const char *def_dir_abspath, svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { svn_config_t *cfg = ctx->config ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; svn_boolean_t use_commit_times; const char *diff3_cmd; const char *preserved_exts_str; const apr_array_header_t *preserved_exts; svn_node_kind_t kind, external_kind; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); /* See if the user wants last-commit timestamps instead of current ones. */ SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); /* Get the external diff3, if any. */ svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_DIFF3_CMD, NULL); if (diff3_cmd != NULL) SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool)); /* See which files the user wants to preserve the extension of when conflict files are made. */ svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); preserved_exts = *preserved_exts_str ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, scratch_pool) : NULL; { const char *wcroot_abspath; SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, local_abspath, scratch_pool, scratch_pool)); /* File externals can only be installed inside the current working copy. So verify if the working copy that contains/will contain the target is the defining abspath, or one of its ancestors */ if (!svn_dirent_is_ancestor(wcroot_abspath, def_dir_abspath)) return svn_error_createf( SVN_ERR_WC_BAD_PATH, NULL, _("Cannot insert a file external defined on '%s' " "into the working copy '%s'."), svn_dirent_local_style(def_dir_abspath, scratch_pool), svn_dirent_local_style(wcroot_abspath, scratch_pool)); } SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath, TRUE, FALSE, scratch_pool)); SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL, ctx->wc_ctx, local_abspath, local_abspath, TRUE, scratch_pool, scratch_pool)); /* If there is a versioned item with this name, ensure it's a file external before working with it. If there is no entry in the working copy, then create an empty file and add it to the working copy. */ if (kind != svn_node_none && kind != svn_node_unknown) { if (external_kind != svn_node_file) { return svn_error_createf( SVN_ERR_CLIENT_FILE_EXTERNAL_OVERWRITE_VERSIONED, 0, _("The file external from '%s' cannot overwrite the existing " "versioned item at '%s'"), url, svn_dirent_local_style(local_abspath, scratch_pool)); } } else { svn_node_kind_t disk_kind; SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool)); if (kind == svn_node_file || kind == svn_node_dir) return svn_error_createf(SVN_ERR_WC_PATH_FOUND, NULL, _("The file external '%s' can not be " "created because the node exists."), svn_dirent_local_style(local_abspath, scratch_pool)); } { const svn_ra_reporter3_t *reporter; void *report_baton; const svn_delta_editor_t *switch_editor; void *switch_baton; svn_client__pathrev_t *switch_loc; svn_revnum_t revnum; apr_array_header_t *inherited_props; const char *dir_abspath; const char *target; svn_dirent_split(&dir_abspath, &target, local_abspath, scratch_pool); /* ### Why do we open a new session? RA_SESSION is a valid ### session -- the caller used it to call svn_ra_check_path on ### this very URL, the caller also did the resolving and ### reparenting that is repeated here. */ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &switch_loc, url, dir_abspath, peg_revision, revision, ctx, scratch_pool)); /* Get the external file's iprops. */ SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "", switch_loc->rev, scratch_pool, scratch_pool)); SVN_ERR(svn_ra_reparent(ra_session, svn_uri_dirname(url, scratch_pool), scratch_pool)); SVN_ERR(svn_wc__get_file_external_editor(&switch_editor, &switch_baton, &revnum, ctx->wc_ctx, local_abspath, def_dir_abspath, switch_loc->url, switch_loc->repos_root_url, switch_loc->repos_uuid, inherited_props, use_commit_times, diff3_cmd, preserved_exts, def_dir_abspath, url, peg_revision, revision, ctx->conflict_func2, ctx->conflict_baton2, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, scratch_pool, scratch_pool)); /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an invalid revnum, that means RA will use the latest revision. */ SVN_ERR(svn_ra_do_switch3(ra_session, &reporter, &report_baton, switch_loc->rev, target, svn_depth_unknown, switch_loc->url, FALSE /* send_copyfrom */, TRUE /* ignore_ancestry */, switch_editor, switch_baton, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__crawl_file_external(ctx->wc_ctx, local_abspath, reporter, report_baton, TRUE, use_commit_times, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, scratch_pool)); if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_completed, scratch_pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); } } return SVN_NO_ERROR; }
/* This is a helper for svn_client__update_internal(), which see for an explanation of most of these parameters. Some stuff that's unique is as follows: ANCHOR_ABSPATH is the local absolute path of the update anchor. This is typically either the same as LOCAL_ABSPATH, or the immediate parent of LOCAL_ABSPATH. If NOTIFY_SUMMARY is set (and there's a notification handler in CTX), transmit the final update summary upon successful completion of the update. */ static svn_error_t * update_internal(svn_revnum_t *result_rev, const char *local_abspath, const char *anchor_abspath, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t depth_is_sticky, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, svn_boolean_t adds_as_modification, svn_boolean_t *timestamp_sleep, svn_boolean_t notify_summary, svn_client_ctx_t *ctx, apr_pool_t *pool) { const svn_delta_editor_t *update_editor; void *update_edit_baton; const svn_ra_reporter3_t *reporter; void *report_baton; const char *anchor_url; const char *corrected_url; const char *target; const char *repos_root; svn_error_t *err; svn_revnum_t revnum; svn_boolean_t use_commit_times; svn_boolean_t sleep_here = FALSE; svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here; svn_boolean_t clean_checkout = FALSE; const char *diff3_cmd; svn_ra_session_t *ra_session; const char *preserved_exts_str; apr_array_header_t *preserved_exts; struct svn_client__dirent_fetcher_baton_t dfb; svn_boolean_t server_supports_depth; svn_boolean_t tree_conflicted; svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING) : NULL; /* An unknown depth can't be sticky. */ if (depth == svn_depth_unknown) depth_is_sticky = FALSE; if (strcmp(local_abspath, anchor_abspath)) target = svn_dirent_basename(local_abspath, pool); else target = ""; /* Get full URL from the ANCHOR. */ SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath, pool, pool)); if (! anchor_url) return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, _("'%s' has no URL"), svn_dirent_local_style(anchor_abspath, pool)); /* Check if our anchor exists in BASE. If it doesn't we can't update. ### For performance reasons this should be handled with the same query ### as retrieving the anchor url. */ SVN_ERR(svn_wc__node_get_base_rev(&revnum, ctx->wc_ctx, anchor_abspath, pool)); /* It does not make sense to update tree-conflict victims. */ err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted, ctx->wc_ctx, local_abspath, pool); if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) { svn_error_clear(err); tree_conflicted = FALSE; } else SVN_ERR(err); if (!SVN_IS_VALID_REVNUM(revnum) || tree_conflicted) { if (ctx->notify_func2) { svn_wc_notify_t *nt; nt = svn_wc_create_notify(local_abspath, tree_conflicted ? svn_wc_notify_skip_conflicted : svn_wc_notify_update_skip_working_only, pool); ctx->notify_func2(ctx->notify_baton2, nt, pool); } return SVN_NO_ERROR; } /* We may need to crop the tree if the depth is sticky */ if (depth_is_sticky && depth < svn_depth_infinity) { svn_node_kind_t target_kind; if (depth == svn_depth_exclude) { SVN_ERR(svn_wc_exclude(ctx->wc_ctx, local_abspath, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); /* Target excluded, we are done now */ return SVN_NO_ERROR; } SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, local_abspath, TRUE, pool)); if (target_kind == svn_node_dir) { SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); } } /* check whether the "clean c/o" optimization is applicable */ SVN_ERR(is_empty_wc(&clean_checkout, local_abspath, anchor_abspath, pool)); /* Get the external diff3, if any. */ svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_DIFF3_CMD, NULL); if (diff3_cmd != NULL) SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, pool)); /* See if the user wants last-commit timestamps instead of current ones. */ SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); /* See which files the user wants to preserve the extension of when conflict files are made. */ svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); preserved_exts = *preserved_exts_str ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, pool) : NULL; /* Let everyone know we're starting a real update (unless we're asked not to). */ if (ctx->notify_func2 && notify_summary) { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_started, pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } /* Open an RA session for the URL */ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url, anchor_url, anchor_abspath, NULL, TRUE, TRUE, ctx, pool)); SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, pool)); /* If we got a corrected URL from the RA subsystem, we'll need to relocate our working copy first. */ if (corrected_url) { const char *current_repos_root; const char *current_uuid; /* To relocate everything inside our repository we need the old and new repos root. ### And we should only perform relocates on the wcroot */ SVN_ERR(svn_wc__node_get_repos_info(¤t_repos_root, ¤t_uuid, ctx->wc_ctx, anchor_abspath, pool, pool)); /* ### Check uuid here before calling relocate? */ SVN_ERR(svn_client_relocate2(anchor_abspath, current_repos_root, repos_root, ignore_externals, ctx, pool)); anchor_url = corrected_url; } /* ### todo: shouldn't svn_client__get_revision_number be able to take a URL as easily as a local path? */ SVN_ERR(svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx, local_abspath, ra_session, revision, pool)); SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, SVN_RA_CAPABILITY_DEPTH, pool)); dfb.ra_session = ra_session; dfb.target_revision = revnum; dfb.anchor_url = anchor_url; /* Fetch the update editor. If REVISION is invalid, that's okay; the RA driver will call editor->set_target_revision later on. */ SVN_ERR(svn_wc_get_update_editor4(&update_editor, &update_edit_baton, &revnum, ctx->wc_ctx, anchor_abspath, target, use_commit_times, depth, depth_is_sticky, allow_unver_obstructions, adds_as_modification, server_supports_depth, clean_checkout, diff3_cmd, preserved_exts, svn_client__dirent_fetcher, &dfb, ctx->conflict_func2, ctx->conflict_baton2, NULL, NULL, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool, pool)); /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an invalid revnum, that means RA will use the latest revision. */ SVN_ERR(svn_ra_do_update2(ra_session, &reporter, &report_baton, revnum, target, (!server_supports_depth || depth_is_sticky ? depth : svn_depth_unknown), FALSE, update_editor, update_edit_baton, pool)); /* Drive the reporter structure, describing the revisions within PATH. When we call reporter->finish_report, the update_editor will be driven by svn_repos_dir_delta2. */ err = svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, report_baton, TRUE, depth, (! depth_is_sticky), (! server_supports_depth), use_commit_times, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool); if (err) { /* Don't rely on the error handling to handle the sleep later, do it now */ svn_io_sleep_for_timestamps(local_abspath, pool); return svn_error_trace(err); } *use_sleep = TRUE; /* We handle externals after the update is complete, so that handling external items (and any errors therefrom) doesn't delay the primary operation. */ if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals)) { apr_hash_t *new_externals; apr_hash_t *new_depths; SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, &new_depths, ctx->wc_ctx, local_abspath, depth, pool, pool)); SVN_ERR(svn_client__handle_externals(new_externals, new_depths, repos_root, local_abspath, depth, use_sleep, ctx, pool)); } if (sleep_here) svn_io_sleep_for_timestamps(local_abspath, pool); /* Let everyone know we're finished here (unless we're asked not to). */ if (ctx->notify_func2 && notify_summary) { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_completed, pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ if (result_rev) *result_rev = revnum; return SVN_NO_ERROR; }
svn_error_t * svn_client__switch_internal(svn_revnum_t *result_rev, const char *path, const char *switch_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, svn_wc_adm_access_t *adm_access, svn_depth_t depth, svn_boolean_t depth_is_sticky, svn_boolean_t *timestamp_sleep, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, svn_client_ctx_t *ctx, apr_pool_t *pool) { const svn_ra_reporter3_t *reporter; void *report_baton; const svn_wc_entry_t *entry; const char *URL, *anchor, *target, *source_root, *switch_rev_url; svn_ra_session_t *ra_session; svn_revnum_t revnum; svn_error_t *err = SVN_NO_ERROR; svn_wc_adm_access_t *dir_access; const svn_boolean_t close_adm_access = ! adm_access; const char *diff3_cmd; svn_boolean_t use_commit_times; svn_boolean_t sleep_here; svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here; const svn_delta_editor_t *switch_editor; void *switch_edit_baton; svn_wc_traversal_info_t *traversal_info = svn_wc_init_traversal_info(pool); const char *preserved_exts_str; apr_array_header_t *preserved_exts; svn_boolean_t server_supports_depth; svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING) : NULL; /* An unknown depth can't be sticky. */ if (depth == svn_depth_unknown) depth_is_sticky = FALSE; /* Do not support the situation of both exclude and switch a target. */ if (depth_is_sticky && depth == svn_depth_exclude) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Cannot both exclude and switch a path")); /* Get the external diff3, if any. */ svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_DIFF3_CMD, NULL); /* See if the user wants last-commit timestamps instead of current ones. */ SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); /* See which files the user wants to preserve the extension of when conflict files are made. */ svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); preserved_exts = *preserved_exts_str ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, pool) : NULL; /* Sanity check. Without these, the switch is meaningless. */ SVN_ERR_ASSERT(path); SVN_ERR_ASSERT(switch_url && (switch_url[0] != '\0')); /* ### Need to lock the whole target tree to invalidate wcprops. Does non-recursive switch really need to invalidate the whole tree? */ if (adm_access) { svn_wc_adm_access_t *a = adm_access; const char *dir_access_path; /* This is a little hacky, but open two new read-only access baton's to get the anchor and target access batons that would be used if a locked access baton was not available. */ SVN_ERR(svn_wc_adm_open_anchor(&adm_access, &dir_access, &target, path, FALSE, -1, ctx->cancel_func, ctx->cancel_baton, pool)); anchor = svn_wc_adm_access_path(adm_access); dir_access_path = svn_wc_adm_access_path(dir_access); SVN_ERR(svn_wc_adm_close2(adm_access, pool)); SVN_ERR(svn_wc_adm_retrieve(&adm_access, a, anchor, pool)); SVN_ERR(svn_wc_adm_retrieve(&dir_access, a, dir_access_path, pool)); } else { SVN_ERR(svn_wc_adm_open_anchor(&adm_access, &dir_access, &target, path, TRUE, -1, ctx->cancel_func, ctx->cancel_baton, pool)); anchor = svn_wc_adm_access_path(adm_access); } SVN_ERR(svn_wc__entry_versioned(&entry, anchor, adm_access, FALSE, pool)); if (! entry->url) return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, _("Directory '%s' has no URL"), svn_path_local_style(anchor, pool)); URL = apr_pstrdup(pool, entry->url); /* Open an RA session to 'source' URL */ SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum, &switch_rev_url, switch_url, adm_access, peg_revision, revision, ctx, pool)); SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_root, pool)); /* Disallow a switch operation to change the repository root of the target. */ if (! svn_path_is_ancestor(source_root, URL)) return svn_error_createf (SVN_ERR_WC_INVALID_SWITCH, NULL, _("'%s'\n" "is not the same repository as\n" "'%s'"), URL, source_root); /* We may need to crop the tree if the depth is sticky */ if (depth_is_sticky && depth < svn_depth_infinity) { const svn_wc_entry_t *target_entry; SVN_ERR(svn_wc_entry( &target_entry, svn_dirent_join(svn_wc_adm_access_path(adm_access), target, pool), adm_access, TRUE, pool)); if (target_entry && target_entry->kind == svn_node_dir) { SVN_ERR(svn_wc_crop_tree(adm_access, target, depth, ctx->notify_func2, ctx->notify_baton2, ctx->cancel_func, ctx->cancel_baton, pool)); } } SVN_ERR(svn_ra_reparent(ra_session, URL, pool)); /* Fetch the switch (update) editor. If REVISION is invalid, that's okay; the RA driver will call editor->set_target_revision() later on. */ SVN_ERR(svn_wc_get_switch_editor3(&revnum, adm_access, target, switch_rev_url, use_commit_times, depth, depth_is_sticky, allow_unver_obstructions, ctx->notify_func2, ctx->notify_baton2, ctx->cancel_func, ctx->cancel_baton, ctx->conflict_func, ctx->conflict_baton, diff3_cmd, preserved_exts, &switch_editor, &switch_edit_baton, traversal_info, pool)); /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an invalid revnum, that means RA will use the latest revision. */ SVN_ERR(svn_ra_do_switch2(ra_session, &reporter, &report_baton, revnum, target, depth, switch_rev_url, switch_editor, switch_edit_baton, pool)); SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, SVN_RA_CAPABILITY_DEPTH, pool)); /* Drive the reporter structure, describing the revisions within PATH. When we call reporter->finish_report, the update_editor will be driven by svn_repos_dir_delta2. We pass in a traversal_info for recording all externals. It shouldn't be needed for a switch if it wasn't for the relative externals of type '../path'. All of those must be resolved to the new location. */ err = svn_wc_crawl_revisions4(path, dir_access, reporter, report_baton, TRUE, depth, (! depth_is_sticky), (! server_supports_depth), use_commit_times, ctx->notify_func2, ctx->notify_baton2, traversal_info, pool); if (err) { /* Don't rely on the error handling to handle the sleep later, do it now */ svn_io_sleep_for_timestamps(path, pool); return err; } *use_sleep = TRUE; /* We handle externals after the switch is complete, so that handling external items (and any errors therefrom) doesn't delay the primary operation. */ if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals)) err = svn_client__handle_externals(adm_access, traversal_info, switch_url, path, source_root, depth, use_sleep, ctx, pool); /* Sleep to ensure timestamp integrity (we do this regardless of errors in the actual switch operation(s)). */ if (sleep_here) svn_io_sleep_for_timestamps(path, pool); /* Return errors we might have sustained. */ if (err) return err; if (close_adm_access) SVN_ERR(svn_wc_adm_close2(adm_access, pool)); /* Let everyone know we're finished here. */ if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(anchor, svn_wc_notify_update_completed, pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ if (result_rev) *result_rev = revnum; return SVN_NO_ERROR; }
static svn_error_t * load_config(svn_ra_serf__session_t *session, apr_hash_t *config_hash, apr_pool_t *pool) { svn_config_t *config, *config_client; const char *server_group; const char *proxy_host = NULL; const char *port_str = NULL; const char *timeout_str = NULL; const char *exceptions; apr_port_t proxy_port; svn_tristate_t chunked_requests; #if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) apr_int64_t log_components; apr_int64_t log_level; #endif if (config_hash) { config = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_SERVERS); config_client = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_CONFIG); } else { config = NULL; config_client = NULL; } SVN_ERR(svn_config_get_bool(config, &session->using_compression, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_COMPRESSION, TRUE)); svn_config_get(config, &timeout_str, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_TIMEOUT, NULL); if (session->auth_baton) { if (config_client) { svn_auth_set_parameter(session->auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG, config_client); } if (config) { svn_auth_set_parameter(session->auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS, config); } } /* Use the default proxy-specific settings if and only if "http-proxy-exceptions" is not set to exclude this host. */ svn_config_get(config, &exceptions, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, ""); if (! svn_cstring_match_glob_list(session->session_url.hostname, svn_cstring_split(exceptions, ",", TRUE, pool))) { svn_config_get(config, &proxy_host, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_HOST, NULL); svn_config_get(config, &port_str, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_PORT, NULL); svn_config_get(config, &session->proxy_username, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, NULL); svn_config_get(config, &session->proxy_password, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, NULL); } /* Load the global ssl settings, if set. */ SVN_ERR(svn_config_get_bool(config, &session->trust_default_ca, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA, TRUE)); svn_config_get(config, &session->ssl_authorities, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, NULL); /* If set, read the flag that tells us to do bulk updates or not. Defaults to skelta updates. */ SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, "auto", svn_tristate_unknown)); /* Load the maximum number of parallel session connections. */ SVN_ERR(svn_config_get_int64(config, &session->max_connections, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, SVN_CONFIG_DEFAULT_OPTION_HTTP_MAX_CONNECTIONS)); /* Should we use chunked transfer encoding. */ SVN_ERR(svn_config_get_tristate(config, &chunked_requests, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS, "auto", svn_tristate_unknown)); #if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) SVN_ERR(svn_config_get_int64(config, &log_components, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS, SERF_LOGCOMP_NONE)); SVN_ERR(svn_config_get_int64(config, &log_level, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_SERF_LOG_LEVEL, SERF_LOG_INFO)); #endif server_group = svn_auth_get_parameter(session->auth_baton, SVN_AUTH_PARAM_SERVER_GROUP); if (server_group) { SVN_ERR(svn_config_get_bool(config, &session->using_compression, server_group, SVN_CONFIG_OPTION_HTTP_COMPRESSION, session->using_compression)); svn_config_get(config, &timeout_str, server_group, SVN_CONFIG_OPTION_HTTP_TIMEOUT, timeout_str); /* Load the group proxy server settings, overriding global settings. We intentionally ignore 'http-proxy-exceptions' here because, well, if this site was an exception, why is there a per-server proxy configuration for it? */ svn_config_get(config, &proxy_host, server_group, SVN_CONFIG_OPTION_HTTP_PROXY_HOST, proxy_host); svn_config_get(config, &port_str, server_group, SVN_CONFIG_OPTION_HTTP_PROXY_PORT, port_str); svn_config_get(config, &session->proxy_username, server_group, SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, session->proxy_username); svn_config_get(config, &session->proxy_password, server_group, SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, session->proxy_password); /* Load the group ssl settings. */ SVN_ERR(svn_config_get_bool(config, &session->trust_default_ca, server_group, SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA, session->trust_default_ca)); svn_config_get(config, &session->ssl_authorities, server_group, SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, session->ssl_authorities); /* Load the group bulk updates flag. */ SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates, server_group, SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, "auto", session->bulk_updates)); /* Load the maximum number of parallel session connections, overriding global values. */ SVN_ERR(svn_config_get_int64(config, &session->max_connections, server_group, SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, session->max_connections)); /* Should we use chunked transfer encoding. */ SVN_ERR(svn_config_get_tristate(config, &chunked_requests, server_group, SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS, "auto", chunked_requests)); #if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) SVN_ERR(svn_config_get_int64(config, &log_components, server_group, SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS, log_components)); SVN_ERR(svn_config_get_int64(config, &log_level, server_group, SVN_CONFIG_OPTION_SERF_LOG_LEVEL, log_level)); #endif } #if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) if (log_components != SERF_LOGCOMP_NONE) { serf_log_output_t *output; apr_status_t status; status = serf_logging_create_stream_output(&output, session->context, (apr_uint32_t)log_level, (apr_uint32_t)log_components, SERF_LOG_DEFAULT_LAYOUT, stderr, pool); if (!status) serf_logging_add_output(session->context, output); } #endif /* Don't allow the http-max-connections value to be larger than our compiled-in limit, or to be too small to operate. Broken functionality and angry administrators are equally undesirable. */ if (session->max_connections > SVN_RA_SERF__MAX_CONNECTIONS_LIMIT) session->max_connections = SVN_RA_SERF__MAX_CONNECTIONS_LIMIT; if (session->max_connections < 2) session->max_connections = 2; /* Parse the connection timeout value, if any. */ session->timeout = apr_time_from_sec(DEFAULT_HTTP_TIMEOUT); if (timeout_str) { char *endstr; const long int timeout = strtol(timeout_str, &endstr, 10); if (*endstr) return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL, _("Invalid config: illegal character in " "timeout value")); if (timeout < 0) return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL, _("Invalid config: negative timeout value")); session->timeout = apr_time_from_sec(timeout); } SVN_ERR_ASSERT(session->timeout >= 0); /* Convert the proxy port value, if any. */ if (port_str) { char *endstr; const long int port = strtol(port_str, &endstr, 10); if (*endstr) return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Invalid URL: illegal character in proxy " "port number")); if (port < 0) return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Invalid URL: negative proxy port number")); if (port > 65535) return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL, _("Invalid URL: proxy port number greater " "than maximum TCP port number 65535")); proxy_port = (apr_port_t) port; } else { proxy_port = 80; } if (proxy_host) { apr_sockaddr_t *proxy_addr; apr_status_t status; status = apr_sockaddr_info_get(&proxy_addr, proxy_host, APR_UNSPEC, proxy_port, 0, session->pool); if (status) { return svn_ra_serf__wrap_err( status, _("Could not resolve proxy server '%s'"), proxy_host); } session->using_proxy = TRUE; serf_config_proxy(session->context, proxy_addr); } else { session->using_proxy = FALSE; } /* Setup detect_chunking and using_chunked_requests based on * the chunked_requests tristate */ if (chunked_requests == svn_tristate_unknown) { session->detect_chunking = TRUE; session->using_chunked_requests = TRUE; } else if (chunked_requests == svn_tristate_true) { session->detect_chunking = FALSE; session->using_chunked_requests = TRUE; } else /* chunked_requests == svn_tristate_false */ { session->detect_chunking = FALSE; session->using_chunked_requests = FALSE; } /* Setup authentication. */ SVN_ERR(load_http_auth_types(pool, config, server_group, &session->authn_types)); serf_config_authn_types(session->context, session->authn_types); serf_config_credentials_callback(session->context, svn_ra_serf__credentials_callback); return SVN_NO_ERROR; }
/* ... Add the paths of any conflict victims to CONFLICTED_PATHS, if that is not null. */ static svn_error_t * switch_internal(svn_revnum_t *result_rev, apr_hash_t *conflicted_paths, const char *local_abspath, const char *anchor_abspath, const char *switch_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t depth_is_sticky, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, svn_boolean_t ignore_ancestry, svn_boolean_t *timestamp_sleep, svn_client_ctx_t *ctx, apr_pool_t *pool) { const svn_ra_reporter3_t *reporter; void *report_baton; const char *anchor_url, *target; svn_client__pathrev_t *switch_loc; svn_ra_session_t *ra_session; svn_revnum_t revnum; const char *diff3_cmd; apr_hash_t *wcroot_iprops; apr_array_header_t *inherited_props; svn_boolean_t use_commit_times; const svn_delta_editor_t *switch_editor; void *switch_edit_baton; const char *preserved_exts_str; apr_array_header_t *preserved_exts; svn_boolean_t server_supports_depth; struct svn_client__dirent_fetcher_baton_t dfb; svn_config_t *cfg = ctx->config ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; /* An unknown depth can't be sticky. */ if (depth == svn_depth_unknown) depth_is_sticky = FALSE; /* Do not support the situation of both exclude and switch a target. */ if (depth == svn_depth_exclude) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Cannot both exclude and switch a path")); /* Get the external diff3, if any. */ svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_DIFF3_CMD, NULL); if (diff3_cmd != NULL) SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, pool)); /* See if the user wants last-commit timestamps instead of current ones. */ SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); { svn_boolean_t has_working; SVN_ERR(svn_wc__node_has_working(&has_working, ctx->wc_ctx, local_abspath, pool)); if (has_working) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Cannot switch '%s' because it is not in the " "repository yet"), svn_dirent_local_style(local_abspath, pool)); } /* See which files the user wants to preserve the extension of when conflict files are made. */ svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); preserved_exts = *preserved_exts_str ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, pool) : NULL; /* Sanity check. Without these, the switch is meaningless. */ SVN_ERR_ASSERT(switch_url && (switch_url[0] != '\0')); if (strcmp(local_abspath, anchor_abspath)) target = svn_dirent_basename(local_abspath, pool); else target = ""; SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath, pool, pool)); if (! anchor_url) return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, _("Directory '%s' has no URL"), svn_dirent_local_style(anchor_abspath, pool)); /* We may need to crop the tree if the depth is sticky */ if (depth_is_sticky && depth < svn_depth_infinity) { svn_node_kind_t target_kind; if (depth == svn_depth_exclude) { SVN_ERR(svn_wc_exclude(ctx->wc_ctx, local_abspath, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); /* Target excluded, we are done now */ return SVN_NO_ERROR; } SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, local_abspath, TRUE, TRUE, pool)); if (target_kind == svn_node_dir) SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); } /* Open an RA session to 'source' URL */ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &switch_loc, switch_url, anchor_abspath, peg_revision, revision, ctx, pool)); /* Disallow a switch operation to change the repository root of the target. */ if (! svn_uri__is_ancestor(switch_loc->repos_root_url, anchor_url)) return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL, _("'%s'\nis not the same repository as\n'%s'"), anchor_url, switch_loc->repos_root_url); /* If we're not ignoring ancestry, then error out if the switch source and target don't have a common ancestory. ### We're acting on the anchor here, not the target. Is that ### okay? */ if (! ignore_ancestry) { svn_client__pathrev_t *target_base_loc, *yca; SVN_ERR(svn_client__wc_node_get_base(&target_base_loc, local_abspath, ctx->wc_ctx, pool, pool)); if (!target_base_loc) yca = NULL; /* Not versioned */ else { SVN_ERR(svn_client__get_youngest_common_ancestor( &yca, switch_loc, target_base_loc, ra_session, ctx, pool, pool)); } if (! yca) return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, _("'%s' shares no common ancestry with '%s'"), switch_url, svn_dirent_local_style(local_abspath, pool)); } wcroot_iprops = apr_hash_make(pool); /* Will the base of LOCAL_ABSPATH require an iprop cache post-switch? If we are switching LOCAL_ABSPATH to the root of the repository then we don't need to cache inherited properties. In all other cases we *might* need to cache iprops. */ if (strcmp(switch_loc->repos_root_url, switch_loc->url) != 0) { svn_boolean_t wc_root; svn_boolean_t needs_iprop_cache = TRUE; SVN_ERR(svn_wc__is_wcroot(&wc_root, ctx->wc_ctx, local_abspath, pool)); /* Switching the WC root to anything but the repos root means we need an iprop cache. */ if (!wc_root) { /* We know we are switching a subtree to something other than the repos root, but if we are unswitching that subtree we don't need an iprops cache. */ const char *target_parent_url; const char *unswitched_url; /* Calculate the URL LOCAL_ABSPATH would have if it was unswitched relative to its parent. */ SVN_ERR(svn_wc__node_get_url(&target_parent_url, ctx->wc_ctx, svn_dirent_dirname(local_abspath, pool), pool, pool)); unswitched_url = svn_path_url_add_component2( target_parent_url, svn_dirent_basename(local_abspath, pool), pool); /* If LOCAL_ABSPATH will be unswitched relative to its parent, then it doesn't need an iprop cache. Note: It doesn't matter if LOCAL_ABSPATH is withing a switched subtree, only if it's the *root* of a switched subtree.*/ if (strcmp(unswitched_url, switch_loc->url) == 0) needs_iprop_cache = FALSE; } if (needs_iprop_cache) { SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "", switch_loc->rev, pool, pool)); svn_hash_sets(wcroot_iprops, local_abspath, inherited_props); } } SVN_ERR(svn_ra_reparent(ra_session, anchor_url, pool)); /* Fetch the switch (update) editor. If REVISION is invalid, that's okay; the RA driver will call editor->set_target_revision() later on. */ SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, SVN_RA_CAPABILITY_DEPTH, pool)); dfb.ra_session = ra_session; dfb.anchor_url = anchor_url; dfb.target_revision = switch_loc->rev; SVN_ERR(svn_wc__get_switch_editor(&switch_editor, &switch_edit_baton, &revnum, ctx->wc_ctx, anchor_abspath, target, switch_loc->url, wcroot_iprops, use_commit_times, depth, depth_is_sticky, allow_unver_obstructions, server_supports_depth, diff3_cmd, preserved_exts, svn_client__dirent_fetcher, &dfb, conflicted_paths ? record_conflict : NULL, conflicted_paths, NULL, NULL, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool, pool)); /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an invalid revnum, that means RA will use the latest revision. */ SVN_ERR(svn_ra_do_switch3(ra_session, &reporter, &report_baton, switch_loc->rev, target, depth_is_sticky ? depth : svn_depth_unknown, switch_loc->url, FALSE /* send_copyfrom_args */, ignore_ancestry, switch_editor, switch_edit_baton, pool, pool)); /* Past this point, we assume the WC is going to be modified so we will * need to sleep for timestamps. */ *timestamp_sleep = TRUE; /* Drive the reporter structure, describing the revisions within LOCAL_ABSPATH. When this calls reporter->finish_report, the reporter will drive the switch_editor. */ SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, report_baton, TRUE, depth, (! depth_is_sticky), (! server_supports_depth), use_commit_times, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); /* We handle externals after the switch is complete, so that handling external items (and any errors therefrom) doesn't delay the primary operation. */ if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals)) { apr_hash_t *new_externals; apr_hash_t *new_depths; SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, &new_depths, ctx->wc_ctx, local_abspath, depth, pool, pool)); SVN_ERR(svn_client__handle_externals(new_externals, new_depths, switch_loc->repos_root_url, local_abspath, depth, timestamp_sleep, ctx, pool)); } /* Let everyone know we're finished here. */ if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(anchor_abspath, svn_wc_notify_update_completed, pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ if (result_rev) *result_rev = revnum; return SVN_NO_ERROR; }
/* This is a helper for svn_client__update_internal(), which see for an explanation of most of these parameters. Some stuff that's unique is as follows: ANCHOR_ABSPATH is the local absolute path of the update anchor. This is typically either the same as LOCAL_ABSPATH, or the immediate parent of LOCAL_ABSPATH. If NOTIFY_SUMMARY is set (and there's a notification handler in CTX), transmit the final update summary upon successful completion of the update. Add the paths of any conflict victims to CONFLICTED_PATHS, if that is not null. */ static svn_error_t * update_internal(svn_revnum_t *result_rev, apr_hash_t *conflicted_paths, const char *local_abspath, const char *anchor_abspath, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t depth_is_sticky, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, svn_boolean_t adds_as_modification, svn_boolean_t *timestamp_sleep, svn_boolean_t notify_summary, svn_client_ctx_t *ctx, apr_pool_t *pool) { const svn_delta_editor_t *update_editor; void *update_edit_baton; const svn_ra_reporter3_t *reporter; void *report_baton; const char *corrected_url; const char *target; const char *repos_root_url; const char *repos_relpath; const char *repos_uuid; const char *anchor_url; svn_revnum_t revnum; svn_boolean_t use_commit_times; svn_boolean_t clean_checkout = FALSE; const char *diff3_cmd; apr_hash_t *wcroot_iprops; svn_opt_revision_t opt_rev; svn_ra_session_t *ra_session; const char *preserved_exts_str; apr_array_header_t *preserved_exts; struct svn_client__dirent_fetcher_baton_t dfb; svn_boolean_t server_supports_depth; svn_boolean_t cropping_target; svn_boolean_t target_conflicted = FALSE; svn_config_t *cfg = ctx->config ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) : NULL; if (result_rev) *result_rev = SVN_INVALID_REVNUM; /* An unknown depth can't be sticky. */ if (depth == svn_depth_unknown) depth_is_sticky = FALSE; if (strcmp(local_abspath, anchor_abspath)) target = svn_dirent_basename(local_abspath, pool); else target = ""; /* Check if our anchor exists in BASE. If it doesn't we can't update. */ SVN_ERR(svn_wc__node_get_base(NULL, NULL, &repos_relpath, &repos_root_url, &repos_uuid, NULL, ctx->wc_ctx, anchor_abspath, TRUE, FALSE, pool, pool)); /* It does not make sense to update conflict victims. */ if (repos_relpath) { svn_error_t *err; svn_boolean_t text_conflicted, prop_conflicted; anchor_url = svn_path_url_add_component2(repos_root_url, repos_relpath, pool); err = svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted, NULL, ctx->wc_ctx, local_abspath, pool); if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) return svn_error_trace(err); svn_error_clear(err); /* tree-conflicts are handled by the update editor */ if (!err && (text_conflicted || prop_conflicted)) target_conflicted = TRUE; } else anchor_url = NULL; if (! anchor_url || target_conflicted) { if (ctx->notify_func2) { svn_wc_notify_t *nt; nt = svn_wc_create_notify(local_abspath, target_conflicted ? svn_wc_notify_skip_conflicted : svn_wc_notify_update_skip_working_only, pool); ctx->notify_func2(ctx->notify_baton2, nt, pool); } return SVN_NO_ERROR; } /* We may need to crop the tree if the depth is sticky */ cropping_target = (depth_is_sticky && depth < svn_depth_infinity); if (cropping_target) { svn_node_kind_t target_kind; if (depth == svn_depth_exclude) { SVN_ERR(svn_wc_exclude(ctx->wc_ctx, local_abspath, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); /* Target excluded, we are done now */ return SVN_NO_ERROR; } SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, local_abspath, TRUE, TRUE, pool)); if (target_kind == svn_node_dir) { SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); } } /* check whether the "clean c/o" optimization is applicable */ SVN_ERR(is_empty_wc(&clean_checkout, local_abspath, anchor_abspath, pool)); /* Get the external diff3, if any. */ svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_DIFF3_CMD, NULL); if (diff3_cmd != NULL) SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, pool)); /* See if the user wants last-commit timestamps instead of current ones. */ SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); /* See which files the user wants to preserve the extension of when conflict files are made. */ svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); preserved_exts = *preserved_exts_str ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, pool) : NULL; /* Let everyone know we're starting a real update (unless we're asked not to). */ if (ctx->notify_func2 && notify_summary) { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_started, pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } /* Open an RA session for the URL */ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url, anchor_url, anchor_abspath, NULL, TRUE, TRUE, ctx, pool, pool)); /* If we got a corrected URL from the RA subsystem, we'll need to relocate our working copy first. */ if (corrected_url) { const char *new_repos_root_url; /* To relocate everything inside our repository we need the old and new repos root. */ SVN_ERR(svn_ra_get_repos_root2(ra_session, &new_repos_root_url, pool)); /* svn_client_relocate2() will check the uuid */ SVN_ERR(svn_client_relocate2(anchor_abspath, repos_root_url, new_repos_root_url, ignore_externals, ctx, pool)); /* Store updated repository root for externals */ repos_root_url = new_repos_root_url; /* ### We should update anchor_loc->repos_uuid too, although currently * we don't use it. */ anchor_url = corrected_url; } /* Resolve unspecified REVISION now, because we need to retrieve the correct inherited props prior to the editor drive and we need to use the same value of HEAD for both. */ opt_rev.kind = revision->kind; opt_rev.value = revision->value; if (opt_rev.kind == svn_opt_revision_unspecified) opt_rev.kind = svn_opt_revision_head; /* ### todo: shouldn't svn_client__get_revision_number be able to take a URL as easily as a local path? */ SVN_ERR(svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx, local_abspath, ra_session, &opt_rev, pool)); SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, SVN_RA_CAPABILITY_DEPTH, pool)); dfb.ra_session = ra_session; dfb.target_revision = revnum; dfb.anchor_url = anchor_url; SVN_ERR(svn_client__get_inheritable_props(&wcroot_iprops, local_abspath, revnum, depth, ra_session, ctx, pool, pool)); /* Fetch the update editor. If REVISION is invalid, that's okay; the RA driver will call editor->set_target_revision later on. */ SVN_ERR(svn_wc__get_update_editor(&update_editor, &update_edit_baton, &revnum, ctx->wc_ctx, anchor_abspath, target, wcroot_iprops, use_commit_times, depth, depth_is_sticky, allow_unver_obstructions, adds_as_modification, server_supports_depth, clean_checkout, diff3_cmd, preserved_exts, svn_client__dirent_fetcher, &dfb, conflicted_paths ? record_conflict : NULL, conflicted_paths, NULL, NULL, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool, pool)); /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an invalid revnum, that means RA will use the latest revision. */ SVN_ERR(svn_ra_do_update3(ra_session, &reporter, &report_baton, revnum, target, (!server_supports_depth || depth_is_sticky ? depth : svn_depth_unknown), FALSE /* send_copyfrom_args */, FALSE /* ignore_ancestry */, update_editor, update_edit_baton, pool, pool)); /* Past this point, we assume the WC is going to be modified so we will * need to sleep for timestamps. */ *timestamp_sleep = TRUE; /* Drive the reporter structure, describing the revisions within PATH. When we call reporter->finish_report, the update_editor will be driven by svn_repos_dir_delta2. */ SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, report_baton, TRUE, depth, (! depth_is_sticky), (! server_supports_depth), use_commit_times, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); /* We handle externals after the update is complete, so that handling external items (and any errors therefrom) doesn't delay the primary operation. */ if ((SVN_DEPTH_IS_RECURSIVE(depth) || cropping_target) && (! ignore_externals)) { apr_hash_t *new_externals; apr_hash_t *new_depths; SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, &new_depths, ctx->wc_ctx, local_abspath, depth, pool, pool)); SVN_ERR(svn_client__handle_externals(new_externals, new_depths, repos_root_url, local_abspath, depth, timestamp_sleep, ctx, pool)); } /* Let everyone know we're finished here (unless we're asked not to). */ if (ctx->notify_func2 && notify_summary) { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_completed, pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ if (result_rev) *result_rev = revnum; return SVN_NO_ERROR; }
/* This implements the `svn_opt_subcommand_t' interface. */ svn_error_t * svn_cl__commit(apr_getopt_t *os, void *baton, apr_pool_t *pool) { svn_error_t *err; svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; apr_array_header_t *targets; apr_array_header_t *condensed_targets; const char *base_dir; svn_config_t *cfg; svn_boolean_t no_unlock = FALSE; struct copy_warning_notify_baton cwnb; SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, opt_state->targets, ctx, FALSE, pool)); SVN_ERR_W(svn_cl__check_targets_are_local_paths(targets), _("Commit targets must be local paths")); /* Add "." if user passed 0 arguments. */ svn_opt_push_implicit_dot_target(targets, pool); SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool)); /* Condense the targets (like commit does)... */ SVN_ERR(svn_dirent_condense_targets(&base_dir, &condensed_targets, targets, TRUE, pool, pool)); if ((! condensed_targets) || (! condensed_targets->nelts)) { const char *parent_dir, *base_name; SVN_ERR(svn_wc_get_actual_target2(&parent_dir, &base_name, ctx->wc_ctx, base_dir, pool, pool)); if (*base_name) base_dir = apr_pstrdup(pool, parent_dir); } if (opt_state->depth == svn_depth_unknown) opt_state->depth = svn_depth_infinity; cfg = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING); if (cfg) SVN_ERR(svn_config_get_bool(cfg, &no_unlock, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_NO_UNLOCK, FALSE)); /* We're creating a new log message baton because we can use our base_dir to store the temp file, instead of the current working directory. The client might not have write access to their working directory, but they better have write access to the directory they're committing. */ SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state, base_dir, ctx->config, pool)); /* Copies are done server-side, and cheaply, which means they're effectively always done with infinite depth. This is a potential cause of confusion for users trying to commit copied subtrees in part by restricting the commit's depth. See issues #3699 and #3752. */ if (opt_state->depth < svn_depth_infinity) { cwnb.wrapped_func = ctx->notify_func2; cwnb.wrapped_baton = ctx->notify_baton2; cwnb.depth = opt_state->depth; cwnb.warned = FALSE; ctx->notify_func2 = copy_warning_notify_func; ctx->notify_baton2 = &cwnb; } /* Commit. */ err = svn_client_commit5(targets, opt_state->depth, no_unlock, opt_state->keep_changelists, TRUE /* commit_as_operations */, opt_state->changelists, opt_state->revprop_table, (opt_state->quiet ? NULL : svn_cl__print_commit_info), NULL, ctx, pool); SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3, err, pool)); return SVN_NO_ERROR; }
svn_error_t * nmap_update_svn_cmdline_setup_auth_baton(svn_auth_baton_t **ab, svn_boolean_t non_interactive, const char *auth_username, const char *auth_password, const char *config_dir, svn_boolean_t no_auth_cache, svn_config_t *cfg, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *pool) { svn_boolean_t store_password_val = TRUE; svn_auth_provider_object_t *provider; /* The whole list of registered providers */ apr_array_header_t *providers = apr_array_make(pool, 12, sizeof(svn_auth_provider_object_t *)); /* The main disk-caching auth providers, for both 'username/password' creds and 'username' creds. */ #if defined(WIN32) && !defined(__MINGW32__) svn_auth_get_windows_simple_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; #endif #ifdef SVN_HAVE_KEYCHAIN_SERVICES svn_auth_get_keychain_simple_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; #endif svn_auth_get_simple_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_username_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; /* The server-cert, client-cert, and client-cert-password providers. */ #if defined(WIN32) && !defined(__MINGW32__) svn_auth_get_windows_ssl_server_trust_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; #endif svn_auth_get_ssl_server_trust_file_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_ssl_client_cert_file_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_ssl_client_cert_pw_file_provider(&provider, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; if (non_interactive == FALSE) { svn_cmdline_prompt_baton_t *pb = NULL; if (cancel_func) { pb = apr_palloc(pool, sizeof(*pb)); pb->cancel_func = cancel_func; pb->cancel_baton = cancel_baton; } /* Two basic prompt providers: username/password, and just username. */ nmap_update_svn_auth_get_simple_prompt_provider(&provider, svn_cmdline_auth_simple_prompt, pb, 2, /* retry limit */ pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_username_prompt_provider (&provider, svn_cmdline_auth_username_prompt, pb, 2, /* retry limit */ pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; /* Three ssl prompt providers, for server-certs, client-certs, and client-cert-passphrases. */ svn_auth_get_ssl_server_trust_prompt_provider (&provider, svn_cmdline_auth_ssl_server_trust_prompt, pb, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_ssl_client_cert_prompt_provider (&provider, svn_cmdline_auth_ssl_client_cert_prompt, pb, 2, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; svn_auth_get_ssl_client_cert_pw_prompt_provider (&provider, svn_cmdline_auth_ssl_client_cert_pw_prompt, pb, 2, pool); APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider; } /* Build an authentication baton to give to libsvn_client. */ svn_auth_open(ab, providers, pool); /* Place any default --username or --password credentials into the auth_baton's run-time parameter hash. */ if (auth_username) svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_USERNAME, auth_username); if (auth_password) svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DEFAULT_PASSWORD, auth_password); /* Same with the --non-interactive option. */ if (non_interactive) svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NON_INTERACTIVE, ""); if (config_dir) svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_CONFIG_DIR, config_dir); SVN_ERR(svn_config_get_bool(cfg, &store_password_val, SVN_CONFIG_SECTION_AUTH, SVN_CONFIG_OPTION_STORE_PASSWORDS, TRUE)); if (! store_password_val) svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, ""); /* There are two different ways the user can disable disk caching of credentials: either via --no-auth-cache, or in the config file ('store-auth-creds = no'). */ SVN_ERR(svn_config_get_bool(cfg, &store_password_val, SVN_CONFIG_SECTION_AUTH, SVN_CONFIG_OPTION_STORE_AUTH_CREDS, TRUE)); if (no_auth_cache || ! store_password_val) svn_auth_set_parameter(*ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, ""); return SVN_NO_ERROR; }
/* Return a memcache in *MEMCACHE_P for FS if it's configured to use memcached, or NULL otherwise. Also, sets *FAIL_STOP to a boolean indicating whether cache errors should be returned to the caller or just passed to the FS warning handler. *CACHE_TXDELTAS, *CACHE_FULLTEXTS and *CACHE_REVPROPS flags will be set according to FS->CONFIG. *CACHE_NAMESPACE receives the cache prefix to use. Use FS->pool for allocating the memcache and CACHE_NAMESPACE, and POOL for temporary allocations. */ static svn_error_t * read_config(svn_memcache_t **memcache_p, svn_boolean_t *fail_stop, const char **cache_namespace, svn_boolean_t *cache_txdeltas, svn_boolean_t *cache_fulltexts, svn_boolean_t *cache_revprops, svn_fs_t *fs, apr_pool_t *pool) { fs_fs_data_t *ffd = fs->fsap_data; SVN_ERR(svn_cache__make_memcache_from_config(memcache_p, ffd->config, fs->pool)); /* No cache namespace by default. I.e. all FS instances share the * cached data. If you specify different namespaces, the data will * share / compete for the same cache memory but keys will not match * across namespaces and, thus, cached data will not be shared between * namespaces. * * Since the namespace will be concatenated with other elements to form * the complete key prefix, we must make sure that the resulting string * is unique and cannot be created by any other combination of elements. */ *cache_namespace = normalize_key_part(svn_hash__get_cstring(fs->config, SVN_FS_CONFIG_FSFS_CACHE_NS, ""), pool); /* don't cache text deltas by default. * Once we reconstructed the fulltexts from the deltas, * these deltas are rarely re-used. Therefore, only tools * like svnadmin will activate this to speed up operations * dump and verify. */ *cache_txdeltas = svn_hash__get_bool(fs->config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, FALSE); /* by default, cache fulltexts. * Most SVN tools care about reconstructed file content. * Thus, this is a reasonable default. * SVN admin tools may set that to FALSE because fulltexts * won't be re-used rendering the cache less effective * by squeezing wanted data out. */ *cache_fulltexts = svn_hash__get_bool(fs->config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, TRUE); /* don't cache revprops by default. * Revprop caching significantly speeds up operations like * svn ls -v. However, it requires synchronization that may * not be available or efficient in the current server setup. * * If the caller chose option "2", enable revprop caching if * the required API support is there to make it efficient. */ if (strcmp(svn_hash__get_cstring(fs->config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, ""), "2")) *cache_revprops = svn_hash__get_bool(fs->config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, FALSE); else *cache_revprops = svn_named_atomic__is_efficient(); return svn_config_get_bool(ffd->config, fail_stop, CONFIG_SECTION_CACHES, CONFIG_OPTION_FAIL_STOP, FALSE); }
static svn_error_t * switch_internal(svn_revnum_t *result_rev, const char *local_abspath, const char *anchor_abspath, const char *switch_url, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t depth_is_sticky, svn_boolean_t ignore_externals, svn_boolean_t allow_unver_obstructions, svn_boolean_t ignore_ancestry, svn_boolean_t *timestamp_sleep, svn_client_ctx_t *ctx, apr_pool_t *pool) { const svn_ra_reporter3_t *reporter; void *report_baton; const char *url, *target, *source_root, *switch_rev_url; svn_ra_session_t *ra_session; svn_revnum_t revnum; svn_error_t *err = SVN_NO_ERROR; const char *diff3_cmd; svn_boolean_t use_commit_times; svn_boolean_t sleep_here = FALSE; svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here; const svn_delta_editor_t *switch_editor; void *switch_edit_baton; const char *preserved_exts_str; apr_array_header_t *preserved_exts; svn_boolean_t server_supports_depth; struct svn_client__dirent_fetcher_baton_t dfb; svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING) : NULL; /* An unknown depth can't be sticky. */ if (depth == svn_depth_unknown) depth_is_sticky = FALSE; /* Do not support the situation of both exclude and switch a target. */ if (depth == svn_depth_exclude) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Cannot both exclude and switch a path")); /* Get the external diff3, if any. */ svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, SVN_CONFIG_OPTION_DIFF3_CMD, NULL); if (diff3_cmd != NULL) SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, pool)); /* See if the user wants last-commit timestamps instead of current ones. */ SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); { svn_boolean_t has_working; SVN_ERR(svn_wc__node_has_working(&has_working, ctx->wc_ctx, local_abspath, pool)); if (has_working) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Cannot switch '%s' because it is not in the " "repository yet"), svn_dirent_local_style(local_abspath, pool)); } /* See which files the user wants to preserve the extension of when conflict files are made. */ svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); preserved_exts = *preserved_exts_str ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, pool) : NULL; /* Sanity check. Without these, the switch is meaningless. */ SVN_ERR_ASSERT(switch_url && (switch_url[0] != '\0')); if (strcmp(local_abspath, anchor_abspath)) target = svn_dirent_basename(local_abspath, pool); else target = ""; SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, anchor_abspath, pool, pool)); if (! url) return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, _("Directory '%s' has no URL"), svn_dirent_local_style(anchor_abspath, pool)); /* We may need to crop the tree if the depth is sticky */ if (depth_is_sticky && depth < svn_depth_infinity) { svn_node_kind_t target_kind; if (depth == svn_depth_exclude) { SVN_ERR(svn_wc_exclude(ctx->wc_ctx, local_abspath, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); /* Target excluded, we are done now */ return SVN_NO_ERROR; } SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, local_abspath, TRUE, pool)); if (target_kind == svn_node_dir) SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool)); } /* Open an RA session to 'source' URL */ SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum, &switch_rev_url, switch_url, anchor_abspath, peg_revision, revision, ctx, pool)); SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_root, pool)); /* Disallow a switch operation to change the repository root of the target. */ if (! svn_uri__is_ancestor(source_root, url)) return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL, _("'%s'\nis not the same repository as\n'%s'"), url, source_root); /* If we're not ignoring ancestry, then error out if the switch source and target don't have a common ancestory. ### We're acting on the anchor here, not the target. Is that ### okay? */ if (! ignore_ancestry) { const char *target_url, *yc_path; svn_revnum_t target_rev, yc_rev; SVN_ERR(svn_wc__node_get_url(&target_url, ctx->wc_ctx, local_abspath, pool, pool)); SVN_ERR(svn_wc__node_get_base_rev(&target_rev, ctx->wc_ctx, local_abspath, pool)); /* ### It would be nice if this function could reuse the existing ra session instead of opening two for its own use. */ SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_path, &yc_rev, switch_rev_url, revnum, target_url, target_rev, ctx, pool)); if (! (yc_path && SVN_IS_VALID_REVNUM(yc_rev))) return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, _("'%s' shares no common ancestry with '%s'"), switch_url, local_abspath); } SVN_ERR(svn_ra_reparent(ra_session, url, pool)); /* Fetch the switch (update) editor. If REVISION is invalid, that's okay; the RA driver will call editor->set_target_revision() later on. */ SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, SVN_RA_CAPABILITY_DEPTH, pool)); dfb.ra_session = ra_session; SVN_ERR(svn_ra_get_session_url(ra_session, &dfb.anchor_url, pool)); dfb.target_revision = revnum; SVN_ERR(svn_wc_get_switch_editor4(&switch_editor, &switch_edit_baton, &revnum, ctx->wc_ctx, anchor_abspath, target, switch_rev_url, use_commit_times, depth, depth_is_sticky, allow_unver_obstructions, server_supports_depth, diff3_cmd, preserved_exts, svn_client__dirent_fetcher, &dfb, ctx->conflict_func2, ctx->conflict_baton2, NULL, NULL, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool, pool)); /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an invalid revnum, that means RA will use the latest revision. */ SVN_ERR(svn_ra_do_switch2(ra_session, &reporter, &report_baton, revnum, target, depth_is_sticky ? depth : svn_depth_unknown, switch_rev_url, switch_editor, switch_edit_baton, pool)); /* Drive the reporter structure, describing the revisions within PATH. When we call reporter->finish_report, the update_editor will be driven by svn_repos_dir_delta2. We pass in an external_func for recording all externals. It shouldn't be needed for a switch if it wasn't for the relative externals of type '../path'. All of those must be resolved to the new location. */ err = svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, report_baton, TRUE, depth, (! depth_is_sticky), (! server_supports_depth), use_commit_times, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool); if (err) { /* Don't rely on the error handling to handle the sleep later, do it now */ svn_io_sleep_for_timestamps(local_abspath, pool); return svn_error_trace(err); } *use_sleep = TRUE; /* We handle externals after the switch is complete, so that handling external items (and any errors therefrom) doesn't delay the primary operation. */ if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals)) { apr_hash_t *new_externals; apr_hash_t *new_depths; SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, &new_depths, ctx->wc_ctx, local_abspath, depth, pool, pool)); SVN_ERR(svn_client__handle_externals(new_externals, new_depths, source_root, local_abspath, depth, use_sleep, ctx, pool)); } /* Sleep to ensure timestamp integrity (we do this regardless of errors in the actual switch operation(s)). */ if (sleep_here) svn_io_sleep_for_timestamps(local_abspath, pool); /* Return errors we might have sustained. */ if (err) return svn_error_trace(err); /* Let everyone know we're finished here. */ if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(anchor_abspath, svn_wc_notify_update_completed, pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ if (result_rev) *result_rev = revnum; return SVN_NO_ERROR; }
svn_error_t * svn_client__get_auto_props(apr_hash_t **properties, const char **mimetype, const char *path, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_config_t *cfg; svn_boolean_t use_autoprops; auto_props_baton_t autoprops; /* initialisation */ autoprops.properties = apr_hash_make(pool); autoprops.filename = svn_path_basename(path, pool); autoprops.pool = pool; autoprops.mimetype = NULL; autoprops.have_executable = FALSE; *properties = autoprops.properties; cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG, APR_HASH_KEY_STRING) : NULL; /* check that auto props is enabled */ SVN_ERR(svn_config_get_bool(cfg, &use_autoprops, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE)); /* search for auto props */ if (use_autoprops) svn_config_enumerate2(cfg, SVN_CONFIG_SECTION_AUTO_PROPS, auto_props_enumerator, &autoprops, pool); /* if mimetype has not been set check the file */ if (! autoprops.mimetype) { SVN_ERR(svn_io_detect_mimetype2(&autoprops.mimetype, path, ctx->mimetypes_map, pool)); if (autoprops.mimetype) apr_hash_set(autoprops.properties, SVN_PROP_MIME_TYPE, strlen(SVN_PROP_MIME_TYPE), svn_string_create(autoprops.mimetype, pool)); } /* Don't automatically set the svn:executable property on added items * on OS400. While OS400 supports the executable permission its use is * inconsistent at best. */ #ifndef AS400 /* if executable has not been set check the file */ if (! autoprops.have_executable) { svn_boolean_t executable = FALSE; SVN_ERR(svn_io_is_file_executable(&executable, path, pool)); if (executable) apr_hash_set(autoprops.properties, SVN_PROP_EXECUTABLE, strlen(SVN_PROP_EXECUTABLE), svn_string_create("", pool)); } #endif *mimetype = autoprops.mimetype; return SVN_NO_ERROR; }