Exemple #1
0
/* Return in *LINK_TARGET_ABSPATH the absolute path the symlink at
 * LOCAL_ABSPATH is pointing to. Perform all allocations in POOL. */
static svn_error_t *
read_link_target(const char **link_target_abspath,
                 const char *local_abspath,
                 apr_pool_t *pool)
{
  svn_string_t *link_target;
  const char *canon_link_target;

  SVN_ERR(svn_io_read_link(&link_target, local_abspath, pool));
  if (link_target->len == 0)
    return svn_error_createf(SVN_ERR_WC_NOT_SYMLINK, NULL,
                             _("The symlink at '%s' points nowhere"),
                             svn_dirent_local_style(local_abspath, pool));

  canon_link_target = svn_dirent_canonicalize(link_target->data, pool);

  /* Treat relative symlinks as relative to LOCAL_ABSPATH's parent. */
  if (!svn_dirent_is_absolute(canon_link_target))
    canon_link_target = svn_dirent_join(svn_dirent_dirname(local_abspath,
                                                           pool),
                                        canon_link_target, pool);

  /* Collapse any .. in the symlink part of the path. */
  if (svn_path_is_backpath_present(canon_link_target))
    SVN_ERR(svn_dirent_get_absolute(link_target_abspath, canon_link_target,
                                    pool));
  else
    *link_target_abspath = canon_link_target;

  return SVN_NO_ERROR;
}
Exemple #2
0
/* Remove the conflict markers of NODE_ABSPATH, that were left over after
   copying NODE_ABSPATH from SRC_ABSPATH.

   Only use this function when you know what you're doing. This function
   explicitly ignores some case insensitivity issues!

   */
static svn_error_t *
remove_node_conflict_markers(svn_wc__db_t *db,
                             const char *src_abspath,
                             const char *node_abspath,
                             apr_pool_t *scratch_pool)
{
  svn_skel_t *conflict;

  SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath,
                                   scratch_pool, scratch_pool));

  /* Do we have conflict markers that should be removed? */
  if (conflict != NULL)
    {
      const apr_array_header_t *markers;
      int i;
      const char *src_dir = svn_dirent_dirname(src_abspath, scratch_pool);
      const char *dst_dir = svn_dirent_dirname(node_abspath, scratch_pool);

      SVN_ERR(svn_wc__conflict_read_markers(&markers, db, src_abspath,
                                            conflict,
                                            scratch_pool, scratch_pool));

      /* No iterpool: Maximum number of possible conflict markers is 4 */
      for (i = 0; markers && (i < markers->nelts); i++)
        {
          const char *marker_abspath;
          const char *child_relpath;
          const char *child_abpath;

          marker_abspath = APR_ARRAY_IDX(markers, i, const char *);

          child_relpath = svn_dirent_is_child(src_dir, marker_abspath, NULL);

          if (child_relpath)
            {
              child_abpath = svn_dirent_join(dst_dir, child_relpath,
                                             scratch_pool);

              SVN_ERR(svn_io_remove_file2(child_abpath, TRUE, scratch_pool));
            }
        }
    }

  return SVN_NO_ERROR;
}
Exemple #3
0
/* Public Interface */
svn_error_t *
svn_wc_diff6(svn_wc_context_t *wc_ctx,
             const char *local_abspath,
             const svn_wc_diff_callbacks4_t *callbacks,
             void *callback_baton,
             svn_depth_t depth,
             svn_boolean_t ignore_ancestry,
             svn_boolean_t show_copies_as_adds,
             svn_boolean_t use_git_diff_format,
             const apr_array_header_t *changelist_filter,
             svn_cancel_func_t cancel_func,
             void *cancel_baton,
             apr_pool_t *scratch_pool)
{
    struct diff_baton eb = { 0 };
    svn_kind_t kind;
    svn_boolean_t get_all;

    SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
    SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath, FALSE,
                                 scratch_pool));

    if (kind == svn_kind_dir)
        eb.anchor_abspath = local_abspath;
    else
        eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool);

    eb.db = wc_ctx->db;
    eb.callbacks = callbacks;
    eb.callback_baton = callback_baton;
    eb.ignore_ancestry = ignore_ancestry;
    eb.show_copies_as_adds = show_copies_as_adds;
    eb.use_git_diff_format = use_git_diff_format;
    eb.empty_file = NULL;
    eb.pool = scratch_pool;

    if (changelist_filter && changelist_filter->nelts)
        SVN_ERR(svn_hash_from_cstring_keys(&eb.changelist_hash, changelist_filter,
                                           scratch_pool));

    if (show_copies_as_adds || use_git_diff_format)
        get_all = TRUE; /* We need unmodified descendants of copies */
    else
        get_all = FALSE;

    /* Walk status handles files and directories */
    SVN_ERR(svn_wc__internal_walk_status(wc_ctx->db, local_abspath, depth,
                                         get_all,
                                         TRUE /* no_ignore */,
                                         FALSE /* ignore_text_mods */,
                                         NULL /* ignore_patterns */,
                                         diff_status_callback, &eb,
                                         cancel_func, cancel_baton,
                                         scratch_pool));

    return SVN_NO_ERROR;
}
Exemple #4
0
void
JNIUtil::putErrorsInTrace(svn_error_t *err,
                          std::vector<jobject> &stackTrace)
{
  if (!err)
    return;

  JNIEnv *env = getEnv();

  // First, put all our child errors in the stack trace
  putErrorsInTrace(err->child, stackTrace);

  // Now, put our own error in the stack trace
  jclass stClazz = env->FindClass("java/lang/StackTraceElement");
  if (isJavaExceptionThrown())
    return;

  static jmethodID ctor_mid = 0;
  if (ctor_mid == 0)
    {
      ctor_mid = env->GetMethodID(stClazz, "<init>",
                                  "(Ljava/lang/String;Ljava/lang/String;"
                                  "Ljava/lang/String;I)V");
      if (isJavaExceptionThrown())
        return;
    }

  jstring jdeclClass = makeJString("native");
  if (isJavaExceptionThrown())
    return;

  char *tmp_path;
  char *path = svn_dirent_dirname(err->file, err->pool);
  while ((tmp_path = strchr(path, '/')))
    *tmp_path = '.';

  jstring jmethodName = makeJString(path);
  if (isJavaExceptionThrown())
    return;

  jstring jfileName = makeJString(svn_dirent_basename(err->file, err->pool));
  if (isJavaExceptionThrown())
    return;

  jobject jelement = env->NewObject(stClazz, ctor_mid, jdeclClass, jmethodName,
                                    jfileName, (jint) err->line);

  stackTrace.push_back(jelement);

  env->DeleteLocalRef(stClazz);
  env->DeleteLocalRef(jdeclClass);
  env->DeleteLocalRef(jmethodName);
  env->DeleteLocalRef(jfileName);
}
Exemple #5
0
svn_error_t *
svn_client_add4(const char *path,
                svn_depth_t depth,
                svn_boolean_t force,
                svn_boolean_t no_ignore,
                svn_boolean_t add_parents,
                svn_client_ctx_t *ctx,
                apr_pool_t *pool)
{
  const char *parent_abspath;
  const char *local_abspath;
  const char *existing_parent_abspath;

  if (svn_path_is_url(path))
    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
                             _("'%s' is not a local path"), path);

  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));

  /* ### this is a hack.
     ### before we switched to absolute paths, if a user tried to do
     ### 'svn add .', PATH would be "" and PARENT_PATH would also be "",
     ### thus emulating the behavior below.  Now that we are using
     ### absolute paths, svn_dirent_dirname() doesn't behave the same way
     ### w.r.t. '.', so we need to include the following hack.  This
     ### behavior is tested in schedule_tests-11. */
  if (path[0] == 0)
    parent_abspath = local_abspath;
  else
    parent_abspath = svn_dirent_dirname(local_abspath, pool);

  existing_parent_abspath = NULL;
  if (add_parents)
    {
      apr_pool_t *subpool;
      const char *existing_parent_abspath2;

      subpool = svn_pool_create(pool);
      SVN_ERR(find_existing_parent(&existing_parent_abspath2, ctx,
                                   parent_abspath, pool, subpool));
      if (strcmp(existing_parent_abspath2, parent_abspath) != 0)
        existing_parent_abspath = existing_parent_abspath2;
      svn_pool_destroy(subpool);
    }

  SVN_WC__CALL_WITH_WRITE_LOCK(
    add(local_abspath, depth, force, no_ignore, existing_parent_abspath,
        ctx, pool),
    ctx->wc_ctx,
    existing_parent_abspath ? existing_parent_abspath : parent_abspath,
    FALSE /* lock_anchor */, pool);
  return SVN_NO_ERROR;
}
Exemple #6
0
svn_error_t *
sbox_wc_add(svn_test__sandbox_t *b, const char *path)
{
  const char *parent_abspath;

  path = sbox_wc_path(b, path);
  parent_abspath = svn_dirent_dirname(path, b->pool);
  SVN_ERR(svn_wc__acquire_write_lock(NULL, b->wc_ctx, parent_abspath, FALSE,
                                     b->pool, b->pool));
  SVN_ERR(svn_wc_add_from_disk2(b->wc_ctx, path, NULL /*props*/,
                                NULL, NULL, b->pool));
  SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, parent_abspath, b->pool));
  return SVN_NO_ERROR;
}
Exemple #7
0
svn_error_t *
sbox_wc_copy(svn_test__sandbox_t *b, const char *from_path, const char *to_path)
{
  const char *parent_abspath;

  from_path = sbox_wc_path(b, from_path);
  to_path = sbox_wc_path(b, to_path);
  parent_abspath = svn_dirent_dirname(to_path, b->pool);
  SVN_ERR(svn_wc__acquire_write_lock(NULL, b->wc_ctx, parent_abspath, FALSE,
                                     b->pool, b->pool));
  SVN_ERR(svn_wc_copy3(b->wc_ctx, from_path, to_path, FALSE,
                       NULL, NULL, NULL, NULL, b->pool));
  SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, parent_abspath, b->pool));
  return SVN_NO_ERROR;
}
Exemple #8
0
/* Go up the directory tree from LOCAL_ABSPATH, looking for a versioned
 * directory.  If found, return its path in *EXISTING_PARENT_ABSPATH.
 * Otherwise, return SVN_ERR_CLIENT_NO_VERSIONED_PARENT. */
static svn_error_t *
find_existing_parent(const char **existing_parent_abspath,
                     svn_client_ctx_t *ctx,
                     const char *local_abspath,
                     apr_pool_t *result_pool,
                     apr_pool_t *scratch_pool)
{
  svn_node_kind_t kind;
  const char *parent_abspath;
  svn_wc_context_t *wc_ctx = ctx->wc_ctx;

  SVN_ERR(svn_wc_read_kind(&kind, wc_ctx, local_abspath, FALSE, scratch_pool));

  if (kind == svn_node_dir)
    {
      svn_boolean_t is_deleted;

      SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted,
                                             wc_ctx, local_abspath,
                                             scratch_pool));
      if (!is_deleted)
        {
          *existing_parent_abspath = apr_pstrdup(result_pool, local_abspath);
          return SVN_NO_ERROR;
        }
    }

  if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
    return svn_error_create(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, NULL);

  if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, scratch_pool),
                        scratch_pool))
    return svn_error_createf(SVN_ERR_RESERVED_FILENAME_SPECIFIED, NULL,
                             _("'%s' ends in a reserved name"),
                             svn_dirent_local_style(local_abspath,
                                                    scratch_pool));

  parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);

  if (ctx->cancel_func)
    SVN_ERR(ctx->cancel_func(ctx->cancel_baton));

  SVN_ERR(find_existing_parent(existing_parent_abspath, ctx, parent_abspath,
                               result_pool, scratch_pool));

  return SVN_NO_ERROR;
}
Exemple #9
0
svn_error_t *
sbox_wc_delete(svn_test__sandbox_t *b, const char *path)
{
  const char *abspath = sbox_wc_path(b, path);
  const char *dir_abspath = svn_dirent_dirname(abspath, b->pool);
  const char *lock_root_abspath;

  SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, b->wc_ctx,
                                     dir_abspath, FALSE,
                                     b->pool, b->pool));
  SVN_ERR(svn_wc_delete4(b->wc_ctx, abspath, FALSE, TRUE,
                         NULL, NULL, /* cancel baton + func */
                         NULL, NULL, /* notify baton + func */
                         b->pool));
  SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, lock_root_abspath, b->pool));
  return SVN_NO_ERROR;
}
Exemple #10
0
/* Write the format number and maximum number of files per directory
   to a new format file in PATH, overwriting a previously existing
   file.  Use POOL for temporary allocation.

   (This implementation is largely stolen from libsvn_fs_fs/fs_fs.c.) */
static svn_error_t *
write_format(const char *path,
             int format,
             int max_files_per_dir,
             apr_pool_t *pool)
{
  const char *contents;

  path = svn_dirent_join(path, "format", pool);

  if (format >= SVN_FS_FS__MIN_LAYOUT_FORMAT_OPTION_FORMAT)
    {
      if (max_files_per_dir)
        contents = apr_psprintf(pool,
                                "%d\n"
                                "layout sharded %d\n",
                                format, max_files_per_dir);
      else
        contents = apr_psprintf(pool,
                                "%d\n"
                                "layout linear",
                                format);
    }
  else
    {
      contents = apr_psprintf(pool, "%d\n", format);
    }

    {
      const char *path_tmp;

      SVN_ERR(svn_io_write_unique(&path_tmp,
                                  svn_dirent_dirname(path, pool),
                                  contents, strlen(contents),
                                  svn_io_file_del_none, pool));

      /* rename the temp file as the real destination */
      SVN_ERR(svn_io_file_rename(path_tmp, path, pool));
    }

  /* And set the perms to make it read only */
  return svn_io_set_file_read_only(path, FALSE, pool);
}
svn_error_t *
svn_config_write_auth_data(apr_hash_t *hash,
                           const char *cred_kind,
                           const char *realmstring,
                           const char *config_dir,
                           apr_pool_t *pool)
{
  svn_stream_t *stream;
  const char *auth_path, *tmp_path;

  SVN_ERR(svn_auth__file_path(&auth_path, cred_kind, realmstring, config_dir,
                              pool));
  if (! auth_path)
    return svn_error_create(SVN_ERR_NO_AUTH_FILE_PATH, NULL,
                            _("Unable to locate auth file"));

  /* Add the realmstring to the hash, so programs (or users) can
     verify exactly which set of credentials this file holds.
     ### What if realmstring key is already in the hash? */
  svn_hash_sets(hash, SVN_CONFIG_REALMSTRING_KEY,
                svn_string_create(realmstring, pool));

  SVN_ERR_W(svn_stream_open_unique(&stream, &tmp_path,
                                   svn_dirent_dirname(auth_path, pool),
                                   svn_io_file_del_on_pool_cleanup,
                                   pool, pool),
            _("Unable to open auth file for writing"));
  SVN_ERR_W(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool),
            apr_psprintf(pool, _("Error writing hash to '%s'"),
                         svn_dirent_local_style(auth_path, pool)));
  SVN_ERR(svn_stream_close(stream));
  SVN_ERR(svn_io_file_rename2(tmp_path, auth_path, FALSE, pool));

  /* To be nice, remove the realmstring from the hash again, just in
     case the caller wants their hash unchanged.
     ### Should we also do this when a write error occurs? */
  svn_hash_sets(hash, SVN_CONFIG_REALMSTRING_KEY, NULL);

  return SVN_NO_ERROR;
}
Exemple #12
0
svn_error_t *
sbox_wc_revert(svn_test__sandbox_t *b, const char *path, svn_depth_t depth)
{
  const char *abspath = sbox_wc_path(b, path);
  const char *dir_abspath;
  const char *lock_root_abspath;

  if (strcmp(abspath, b->wc_abspath))
    dir_abspath = svn_dirent_dirname(abspath, b->pool);
  else
    dir_abspath = abspath;

  SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, b->wc_ctx,
                                     dir_abspath, FALSE /* lock_anchor */,
                                     b->pool, b->pool));
  SVN_ERR(svn_wc_revert4(b->wc_ctx, abspath, depth, FALSE, NULL,
                         NULL, NULL, /* cancel baton + func */
                         NULL, NULL, /* notify baton + func */
                         b->pool));
  SVN_ERR(svn_wc__release_write_lock(b->wc_ctx, lock_root_abspath, b->pool));
  return SVN_NO_ERROR;
}
Exemple #13
0
static svn_error_t *
file_textdelta(void *file_baton,
               const char *base_checksum,
               apr_pool_t *result_pool,
               svn_txdelta_window_handler_t *handler,
               void **handler_baton)
{
  struct file_baton_t *fb = file_baton;
  const char *target_dir = svn_dirent_dirname(fb->local_abspath, fb->pool);
  svn_error_t *err;
  svn_stream_t *source;
  svn_stream_t *target;

  SVN_ERR_ASSERT(! fb->writing_file);

  err = svn_stream_open_readonly(&source, fb->local_abspath,
                                 fb->pool, fb->pool);
  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
    {
      svn_error_clear(err);
      source = svn_stream_empty(fb->pool);
    }
  else
    SVN_ERR(err);

  SVN_ERR(svn_stream_open_unique(&target, &fb->writing_file,
                                 target_dir, svn_io_file_del_none,
                                 fb->pool, fb->pool));

  svn_txdelta_apply(source,
                    target,
                    fb->digest,
                    fb->local_abspath,
                    fb->pool,
                    /* Provide the handler directly */
                    handler, handler_baton);

  return SVN_NO_ERROR;
}
Exemple #14
0
svn_error_t *
svn_wc_copy3(svn_wc_context_t *wc_ctx,
             const char *src_abspath,
             const char *dst_abspath,
             svn_boolean_t metadata_only,
             svn_cancel_func_t cancel_func,
             void *cancel_baton,
             svn_wc_notify_func2_t notify_func,
             void *notify_baton,
             apr_pool_t *scratch_pool)
{
  /* Verify that we have the required write lock. */
  SVN_ERR(svn_wc__write_check(wc_ctx->db,
                              svn_dirent_dirname(dst_abspath, scratch_pool),
                              scratch_pool));

  return svn_error_trace(copy_or_move(NULL, wc_ctx, src_abspath, dst_abspath,
                                      metadata_only, FALSE /* is_move */,
                                      TRUE /* allow_mixed_revisions */,
                                      cancel_func, cancel_baton,
                                      notify_func, notify_baton,
                                      scratch_pool));
}
Exemple #15
0
/*
 * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
 * either return an error to be displayed, or set *EXIT_CODE to non-zero and
 * return SVN_NO_ERROR.
 */
static svn_error_t *
sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
{
  enum run_mode run_mode = run_mode_unspecified;
  svn_boolean_t foreground = FALSE;
  apr_socket_t *sock;
  apr_sockaddr_t *sa;
  svn_error_t *err;
  apr_getopt_t *os;
  int opt;
  serve_params_t params;
  const char *arg;
  apr_status_t status;
#ifndef WIN32
  apr_proc_t proc;
#endif
  svn_boolean_t is_multi_threaded;
  enum connection_handling_mode handling_mode = CONNECTION_DEFAULT;
  svn_boolean_t cache_fulltexts = TRUE;
  svn_boolean_t cache_txdeltas = TRUE;
  svn_boolean_t cache_revprops = FALSE;
  svn_boolean_t use_block_read = FALSE;
  apr_uint16_t port = SVN_RA_SVN_PORT;
  const char *host = NULL;
  int family = APR_INET;
  apr_int32_t sockaddr_info_flags = 0;
#if APR_HAVE_IPV6
  svn_boolean_t prefer_v6 = FALSE;
#endif
  svn_boolean_t quiet = FALSE;
  svn_boolean_t is_version = FALSE;
  int mode_opt_count = 0;
  int handling_opt_count = 0;
  const char *config_filename = NULL;
  const char *pid_filename = NULL;
  const char *log_filename = NULL;
  svn_node_kind_t kind;
  apr_size_t min_thread_count = THREADPOOL_MIN_SIZE;
  apr_size_t max_thread_count = THREADPOOL_MAX_SIZE;
#ifdef SVN_HAVE_SASL
  SVN_ERR(cyrus_init(pool));
#endif

  /* Check library versions */
  SVN_ERR(check_lib_versions());

  /* Initialize the FS library. */
  SVN_ERR(svn_fs_initialize(pool));

  SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));

  params.root = "/";
  params.tunnel = FALSE;
  params.tunnel_user = NULL;
  params.read_only = FALSE;
  params.base = NULL;
  params.cfg = NULL;
  params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
  params.logger = NULL;
  params.config_pool = NULL;
  params.authz_pool = NULL;
  params.fs_config = NULL;
  params.vhost = FALSE;
  params.username_case = CASE_ASIS;
  params.memory_cache_size = (apr_uint64_t)-1;
  params.zero_copy_limit = 0;
  params.error_check_interval = 4096;

  while (1)
    {
      status = apr_getopt_long(os, svnserve__options, &opt, &arg);
      if (APR_STATUS_IS_EOF(status))
        break;
      if (status != APR_SUCCESS)
        {
          usage(argv[0], pool);
          *exit_code = EXIT_FAILURE;
          return SVN_NO_ERROR;
        }
      switch (opt)
        {
        case '6':
#if APR_HAVE_IPV6
          prefer_v6 = TRUE;
#endif
          /* ### Maybe error here if we don't have IPV6 support? */
          break;

        case 'h':
          help(pool);
          return SVN_NO_ERROR;

        case 'q':
          quiet = TRUE;
          break;

        case SVNSERVE_OPT_VERSION:
          is_version = TRUE;
          break;

        case 'd':
          if (run_mode != run_mode_daemon)
            {
              run_mode = run_mode_daemon;
              mode_opt_count++;
            }
          break;

        case SVNSERVE_OPT_FOREGROUND:
          foreground = TRUE;
          break;

        case SVNSERVE_OPT_SINGLE_CONN:
          handling_mode = connection_mode_single;
          handling_opt_count++;
          break;

        case 'i':
          if (run_mode != run_mode_inetd)
            {
              run_mode = run_mode_inetd;
              mode_opt_count++;
            }
          break;

        case SVNSERVE_OPT_LISTEN_PORT:
          {
            apr_uint64_t val;

            err = svn_cstring_strtoui64(&val, arg, 0, APR_UINT16_MAX, 10);
            if (err)
              return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, err,
                                       _("Invalid port '%s'"), arg);
            port = (apr_uint16_t)val;
          }
          break;

        case SVNSERVE_OPT_LISTEN_HOST:
          host = arg;
          break;

        case 't':
          if (run_mode != run_mode_tunnel)
            {
              run_mode = run_mode_tunnel;
              mode_opt_count++;
            }
          break;

        case SVNSERVE_OPT_TUNNEL_USER:
          params.tunnel_user = arg;
          break;

        case 'X':
          if (run_mode != run_mode_listen_once)
            {
              run_mode = run_mode_listen_once;
              mode_opt_count++;
            }
          break;

        case 'r':
          SVN_ERR(svn_utf_cstring_to_utf8(&params.root, arg, pool));

          SVN_ERR(svn_io_check_resolved_path(params.root, &kind, pool));
          if (kind != svn_node_dir)
            {
              return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
                       _("Root path '%s' does not exist "
                         "or is not a directory"), params.root);
            }

          params.root = svn_dirent_internal_style(params.root, pool);
          SVN_ERR(svn_dirent_get_absolute(&params.root, params.root, pool));
          break;

        case 'R':
          params.read_only = TRUE;
          break;

        case 'T':
          handling_mode = connection_mode_thread;
          handling_opt_count++;
          break;

        case 'c':
          params.compression_level = atoi(arg);
          if (params.compression_level < SVN_DELTA_COMPRESSION_LEVEL_NONE)
            params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
          if (params.compression_level > SVN_DELTA_COMPRESSION_LEVEL_MAX)
            params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_MAX;
          break;

        case 'M':
          params.memory_cache_size = 0x100000 * apr_strtoi64(arg, NULL, 0);
          break;

        case SVNSERVE_OPT_CACHE_TXDELTAS:
          cache_txdeltas = svn_tristate__from_word(arg) == svn_tristate_true;
          break;

        case SVNSERVE_OPT_CACHE_FULLTEXTS:
          cache_fulltexts = svn_tristate__from_word(arg) == svn_tristate_true;
          break;

        case SVNSERVE_OPT_CACHE_REVPROPS:
          cache_revprops = svn_tristate__from_word(arg) == svn_tristate_true;
          break;

        case SVNSERVE_OPT_BLOCK_READ:
          use_block_read = svn_tristate__from_word(arg) == svn_tristate_true;
          break;

        case SVNSERVE_OPT_CLIENT_SPEED:
          {
            apr_size_t bandwidth = (apr_size_t)apr_strtoi64(arg, NULL, 0);

            /* for slower clients, don't try anything fancy */
            if (bandwidth >= 1000)
              {
                /* block other clients for at most 1 ms (at full bandwidth).
                   Note that the send buffer is 16kB anyways. */
                params.zero_copy_limit = bandwidth * 120;

                /* check for aborted connections at the same rate */
                params.error_check_interval = bandwidth * 120;
              }
          }
          break;

        case SVNSERVE_OPT_MIN_THREADS:
          min_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
          break;

        case SVNSERVE_OPT_MAX_THREADS:
          max_thread_count = (apr_size_t)apr_strtoi64(arg, NULL, 0);
          break;

#ifdef WIN32
        case SVNSERVE_OPT_SERVICE:
          if (run_mode != run_mode_service)
            {
              run_mode = run_mode_service;
              mode_opt_count++;
            }
          break;
#endif

        case SVNSERVE_OPT_CONFIG_FILE:
          SVN_ERR(svn_utf_cstring_to_utf8(&config_filename, arg, pool));
          config_filename = svn_dirent_internal_style(config_filename, pool);
          SVN_ERR(svn_dirent_get_absolute(&config_filename, config_filename,
                                          pool));
          break;

        case SVNSERVE_OPT_PID_FILE:
          SVN_ERR(svn_utf_cstring_to_utf8(&pid_filename, arg, pool));
          pid_filename = svn_dirent_internal_style(pid_filename, pool);
          SVN_ERR(svn_dirent_get_absolute(&pid_filename, pid_filename, pool));
          break;

         case SVNSERVE_OPT_VIRTUAL_HOST:
           params.vhost = TRUE;
           break;

         case SVNSERVE_OPT_LOG_FILE:
          SVN_ERR(svn_utf_cstring_to_utf8(&log_filename, arg, pool));
          log_filename = svn_dirent_internal_style(log_filename, pool);
          SVN_ERR(svn_dirent_get_absolute(&log_filename, log_filename, pool));
          break;

        }
    }

  if (is_version)
    {
      SVN_ERR(version(quiet, pool));
      return SVN_NO_ERROR;
    }

  if (os->ind != argc)
    {
      usage(argv[0], pool);
      *exit_code = EXIT_FAILURE;
      return SVN_NO_ERROR;
    }

  if (mode_opt_count != 1)
    {
      svn_error_clear(svn_cmdline_fputs(
#ifdef WIN32
                      _("You must specify exactly one of -d, -i, -t, "
                        "--service or -X.\n"),
#else
                      _("You must specify exactly one of -d, -i, -t or -X.\n"),
#endif
                       stderr, pool));
      usage(argv[0], pool);
      *exit_code = EXIT_FAILURE;
      return SVN_NO_ERROR;
    }

  if (handling_opt_count > 1)
    {
      svn_error_clear(svn_cmdline_fputs(
                      _("You may only specify one of -T or --single-thread\n"),
                      stderr, pool));
      usage(argv[0], pool);
      *exit_code = EXIT_FAILURE;
      return SVN_NO_ERROR;
    }

  /* construct object pools */
  is_multi_threaded = handling_mode == connection_mode_thread;
  params.fs_config = apr_hash_make(pool);
  svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS,
                cache_txdeltas ? "1" :"0");
  svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
                cache_fulltexts ? "1" :"0");
  svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
                cache_revprops ? "2" :"0");
  svn_hash_sets(params.fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ,
                use_block_read ? "1" :"0");

  SVN_ERR(svn_repos__config_pool_create(&params.config_pool,
                                        is_multi_threaded,
                                        pool));
  SVN_ERR(svn_repos__authz_pool_create(&params.authz_pool,
                                       params.config_pool,
                                       is_multi_threaded,
                                       pool));

  /* If a configuration file is specified, load it and any referenced
   * password and authorization files. */
  if (config_filename)
    {
      params.base = svn_dirent_dirname(config_filename, pool);

      SVN_ERR(svn_repos__config_pool_get(&params.cfg, NULL,
                                         params.config_pool,
                                         config_filename,
                                         TRUE, /* must_exist */
                                         FALSE, /* names_case_sensitive */
                                         NULL,
                                         pool));
    }

  if (log_filename)
    SVN_ERR(logger__create(&params.logger, log_filename, pool));
  else if (run_mode == run_mode_listen_once)
    SVN_ERR(logger__create_for_stderr(&params.logger, pool));

  if (params.tunnel_user && run_mode != run_mode_tunnel)
    {
      return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
               _("Option --tunnel-user is only valid in tunnel mode"));
    }

  if (run_mode == run_mode_inetd || run_mode == run_mode_tunnel)
    {
      apr_pool_t *connection_pool;
      svn_ra_svn_conn_t *conn;
      svn_stream_t *stdin_stream;
      svn_stream_t *stdout_stream;

      params.tunnel = (run_mode == run_mode_tunnel);
      apr_pool_cleanup_register(pool, pool, apr_pool_cleanup_null,
                                redirect_stdout);

      SVN_ERR(svn_stream_for_stdin(&stdin_stream, pool));
      SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));

      /* Use a subpool for the connection to ensure that if SASL is used
       * the pool cleanup handlers that call sasl_dispose() (connection_pool)
       * and sasl_done() (pool) are run in the right order. See issue #3664. */
      connection_pool = svn_pool_create(pool);
      conn = svn_ra_svn_create_conn4(NULL, stdin_stream, stdout_stream,
                                     params.compression_level,
                                     params.zero_copy_limit,
                                     params.error_check_interval,
                                     connection_pool);
      err = serve(conn, &params, connection_pool);
      svn_pool_destroy(connection_pool);

      return err;
    }

#ifdef WIN32
  /* If svnserve needs to run as a Win32 service, then we need to
     coordinate with the Service Control Manager (SCM) before
     continuing.  This function call registers the svnserve.exe
     process with the SCM, waits for the "start" command from the SCM
     (which will come very quickly), and confirms that those steps
     succeeded.

     After this call succeeds, the service is free to run.  At some
     point in the future, the SCM will send a message to the service,
     requesting that it stop.  This is translated into a call to
     winservice_notify_stop().  The service is then responsible for
     cleanly terminating.

     We need to do this before actually starting the service logic
     (opening files, sockets, etc.) because the SCM wants you to
     connect *first*, then do your service-specific logic.  If the
     service process takes too long to connect to the SCM, then the
     SCM will decide that the service is busted, and will give up on
     it.
     */
  if (run_mode == run_mode_service)
    {
      err = winservice_start();
      if (err)
        {
          svn_handle_error2(err, stderr, FALSE, "svnserve: ");

          /* This is the most common error.  It means the user started
             svnserve from a shell, and specified the --service
             argument.  svnserve cannot be started, as a service, in
             this way.  The --service argument is valid only valid if
             svnserve is started by the SCM. */
          if (err->apr_err ==
              APR_FROM_OS_ERROR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT))
            {
              svn_error_clear(svn_cmdline_fprintf(stderr, pool,
                  _("svnserve: The --service flag is only valid if the"
                    " process is started by the Service Control Manager.\n")));
            }

          svn_error_clear(err);
          *exit_code = EXIT_FAILURE;
          return SVN_NO_ERROR;
        }

      /* The service is now in the "starting" state.  Before the SCM will
         consider the service "started", this thread must call the
         winservice_running() function. */
    }
#endif /* WIN32 */

  /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get
     APR_UNSPEC, because it may give us back an IPV6 address even if we can't
     create IPV6 sockets. */

#if APR_HAVE_IPV6
#ifdef MAX_SECS_TO_LINGER
  /* ### old APR interface */
  status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, pool);
#else
  status = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP,
                             pool);
#endif
  if (status == 0)
    {
      apr_socket_close(sock);
      family = APR_UNSPEC;

      if (prefer_v6)
        {
          if (host == NULL)
            host = "::";
          sockaddr_info_flags = APR_IPV6_ADDR_OK;
        }
      else
        {
          if (host == NULL)
            host = "0.0.0.0";
          sockaddr_info_flags = APR_IPV4_ADDR_OK;
        }
    }
#endif

  status = apr_sockaddr_info_get(&sa, host, family, port,
                                 sockaddr_info_flags, pool);
  if (status)
    {
      return svn_error_wrap_apr(status, _("Can't get address info"));
    }


#ifdef MAX_SECS_TO_LINGER
  /* ### old APR interface */
  status = apr_socket_create(&sock, sa->family, SOCK_STREAM, pool);
#else
  status = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP,
                             pool);
#endif
  if (status)
    {
      return svn_error_wrap_apr(status, _("Can't create server socket"));
    }

  /* Prevents "socket in use" errors when server is killed and quickly
   * restarted. */
  status = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
  if (status)
    {
      return svn_error_wrap_apr(status, _("Can't set options on server socket"));
    }

  status = apr_socket_bind(sock, sa);
  if (status)
    {
      return svn_error_wrap_apr(status, _("Can't bind server socket"));
    }

  status = apr_socket_listen(sock, ACCEPT_BACKLOG);
  if (status)
    {
      return svn_error_wrap_apr(status, _("Can't listen on server socket"));
    }

#if APR_HAS_FORK
  if (run_mode != run_mode_listen_once && !foreground)
    /* ### ignoring errors... */
    apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);

  apr_signal(SIGCHLD, sigchld_handler);
#endif

#ifdef SIGPIPE
  /* Disable SIGPIPE generation for the platforms that have it. */
  apr_signal(SIGPIPE, SIG_IGN);
#endif

#ifdef SIGXFSZ
  /* Disable SIGXFSZ generation for the platforms that have it, otherwise
   * working with large files when compiled against an APR that doesn't have
   * large file support will crash the program, which is uncool. */
  apr_signal(SIGXFSZ, SIG_IGN);
#endif

  if (pid_filename)
    SVN_ERR(write_pid_file(pid_filename, pool));

#ifdef WIN32
  status = apr_os_sock_get(&winservice_svnserve_accept_socket, sock);
  if (status)
    winservice_svnserve_accept_socket = INVALID_SOCKET;

  /* At this point, the service is "running".  Notify the SCM. */
  if (run_mode == run_mode_service)
    winservice_running();
#endif

  /* Configure FS caches for maximum efficiency with svnserve.
   * For pre-forked (i.e. multi-processed) mode of operation,
   * keep the per-process caches smaller than the default.
   * Also, apply the respective command line parameters, if given. */
  {
    svn_cache_config_t settings = *svn_cache_config_get();

    if (params.memory_cache_size != -1)
      settings.cache_size = params.memory_cache_size;

    settings.single_threaded = TRUE;
    if (handling_mode == connection_mode_thread)
      {
#if APR_HAS_THREADS
        settings.single_threaded = FALSE;
#else
        /* No requests will be processed at all
         * (see "switch (handling_mode)" code further down).
         * But if they were, some other synchronization code
         * would need to take care of securing integrity of
         * APR-based structures. That would include our caches.
         */
#endif
      }

    svn_cache_config_set(&settings);
  }

#if APR_HAS_THREADS
  SVN_ERR(svn_root_pools__create(&connection_pools));

  if (handling_mode == connection_mode_thread)
    {
      /* create the thread pool with a valid range of threads */
      if (max_thread_count < 1)
        max_thread_count = 1;
      if (min_thread_count > max_thread_count)
        min_thread_count = max_thread_count;

      status = apr_thread_pool_create(&threads,
                                      min_thread_count,
                                      max_thread_count,
                                      pool);
      if (status)
        {
          return svn_error_wrap_apr(status, _("Can't create thread pool"));
        }

      /* let idle threads linger for a while in case more requests are
         coming in */
      apr_thread_pool_idle_wait_set(threads, THREADPOOL_THREAD_IDLE_LIMIT);

      /* don't queue requests unless we reached the worker thread limit */
      apr_thread_pool_threshold_set(threads, 0);
    }
  else
    {
      threads = NULL;
    }
#endif

  while (1)
    {
      connection_t *connection = NULL;
      SVN_ERR(accept_connection(&connection, sock, &params, handling_mode,
                                pool));
      if (run_mode == run_mode_listen_once)
        {
          err = serve_socket(connection, connection->pool);
          close_connection(connection);
          return err;
        }

      switch (handling_mode)
        {
        case connection_mode_fork:
#if APR_HAS_FORK
          status = apr_proc_fork(&proc, connection->pool);
          if (status == APR_INCHILD)
            {
              /* the child would't listen to the main server's socket */
              apr_socket_close(sock);

              /* serve_socket() logs any error it returns, so ignore it. */
              svn_error_clear(serve_socket(connection, connection->pool));
              close_connection(connection);
              return SVN_NO_ERROR;
            }
          else if (status != APR_INPARENT)
            {
              err = svn_error_wrap_apr(status, "apr_proc_fork");
              logger__log_error(params.logger, err, NULL, NULL);
              svn_error_clear(err);
            }
#endif
          break;

        case connection_mode_thread:
          /* Create a detached thread for each connection.  That's not a
             particularly sophisticated strategy for a threaded server, it's
             little different from forking one process per connection. */
#if APR_HAS_THREADS
          attach_connection(connection);

          status = apr_thread_pool_push(threads, serve_thread, connection,
                                        0, NULL);
          if (status)
            {
              return svn_error_wrap_apr(status, _("Can't push task"));
            }
#endif
          break;

        case connection_mode_single:
          /* Serve one connection at a time. */
          /* serve_socket() logs any error it returns, so ignore it. */
          svn_error_clear(serve_socket(connection, connection->pool));
        }

      close_connection(connection);
    }

  /* NOTREACHED */
}
Exemple #16
0
/* Public Interface */
svn_error_t *
svn_wc_diff6(svn_wc_context_t *wc_ctx,
             const char *local_abspath,
             const svn_wc_diff_callbacks4_t *callbacks,
             void *callback_baton,
             svn_depth_t depth,
             svn_boolean_t ignore_ancestry,
             svn_boolean_t show_copies_as_adds,
             svn_boolean_t use_git_diff_format,
             const apr_array_header_t *changelist_filter,
             svn_cancel_func_t cancel_func,
             void *cancel_baton,
             apr_pool_t *scratch_pool)
{
  struct diff_baton eb = { 0 };
  svn_node_kind_t kind;
  svn_boolean_t get_all;
  const svn_diff_tree_processor_t *processor;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
  SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath,
                               FALSE /* allow_missing */,
                               TRUE /* show_deleted */,
                               FALSE /* show_hidden */,
                               scratch_pool));

  if (kind == svn_node_dir)
    eb.anchor_abspath = local_abspath;
  else
    eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool);

  SVN_ERR(svn_wc__wrap_diff_callbacks(&processor,
                                      callbacks, callback_baton, TRUE,
                                      scratch_pool, scratch_pool));

  if (use_git_diff_format)
    show_copies_as_adds = TRUE;
  if (show_copies_as_adds)
    ignore_ancestry = FALSE;



  /*
  if (reverse_order)
    processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
   */

  if (! show_copies_as_adds && !use_git_diff_format)
    processor = svn_diff__tree_processor_copy_as_changed_create(processor,
                                                                scratch_pool);

  eb.db = wc_ctx->db;
  eb.processor = processor;
  eb.ignore_ancestry = ignore_ancestry;
  eb.show_copies_as_adds = show_copies_as_adds;
  eb.pool = scratch_pool;

  if (changelist_filter && changelist_filter->nelts)
    SVN_ERR(svn_hash_from_cstring_keys(&eb.changelist_hash, changelist_filter,
                                       scratch_pool));

  if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry)
    get_all = TRUE; /* We need unmodified descendants of copies */
  else
    get_all = FALSE;

  /* Walk status handles files and directories */
  SVN_ERR(svn_wc__internal_walk_status(wc_ctx->db, local_abspath, depth,
                                       get_all,
                                       TRUE /* no_ignore */,
                                       FALSE /* ignore_text_mods */,
                                       NULL /* ignore_patterns */,
                                       diff_status_callback, &eb,
                                       cancel_func, cancel_baton,
                                       scratch_pool));

  /* Close the remaining open directories */
  while (eb.cur)
    {
      struct node_state_t *ns = eb.cur;

      if (!ns->skip)
        {
          if (ns->propchanges)
            SVN_ERR(processor->dir_changed(ns->relpath,
                                           ns->left_src,
                                           ns->right_src,
                                           ns->left_props,
                                           ns->right_props,
                                           ns->propchanges,
                                           ns->baton,
                                           processor,
                                           ns->pool));
          else
            SVN_ERR(processor->dir_closed(ns->relpath,
                                          ns->left_src,
                                          ns->right_src,
                                          ns->baton,
                                          processor,
                                          ns->pool));
        }
      eb.cur = ns->parent;
      svn_pool_clear(ns->pool);
    }

  return SVN_NO_ERROR;
}
Exemple #17
0
/**
 * Initialize the environment for all requests.
 * @param env   the JNI environment for this request
 */
bool JNIUtil::JNIGlobalInit(JNIEnv *env)
{
  // This method has to be run only once during the run a program.
  static bool run = false;
  svn_error_t *err;
  if (run) // already run
    return true;

  run = true;

  // Do not run this part more than one time.  This leaves a small
  // time window when two threads create their first SVNClient and
  // SVNAdmin at the same time, but I do not see a better option
  // without APR already initialized
  if (g_inInit)
    return false;

  g_inInit = true;
  g_initEnv = env;

  apr_status_t status;



  /* Initialize the APR subsystem, and register an atexit() function
   * to Uninitialize that subsystem at program exit. */
  status = apr_initialize();
  if (status)
    {
      if (stderr)
        {
          char buf[1024];
          apr_strerror(status, buf, sizeof(buf) - 1);
          fprintf(stderr,
                  "%s: error: cannot initialize APR: %s\n",
                  "svnjavahl", buf);
        }
      return FALSE;
    }

  /* This has to happen before any pools are created. */
  if ((err = svn_dso_initialize2()))
    {
      if (stderr && err->message)
        fprintf(stderr, "%s", err->message);

      svn_error_clear(err);
      return FALSE;
    }

  if (0 > atexit(apr_terminate))
    {
      if (stderr)
        fprintf(stderr,
                "%s: error: atexit registration failed\n",
                "svnjavahl");
      return FALSE;
    }

  /* Create our top-level pool. */
  g_pool = svn_pool_create(NULL);

  apr_allocator_t* allocator = apr_pool_allocator_get(g_pool);

  if (allocator)
    {
      /* Keep a maximum of 1 free block, to release memory back to the JVM
         (and other modules). */
      apr_allocator_max_free_set(allocator, 1);
    }

  svn_utf_initialize2(FALSE, g_pool); /* Optimize character conversions */
  svn_fs_initialize(g_pool); /* Avoid some theoretical issues */
  svn_ra_initialize(g_pool);

  /* We shouldn't fill the JVMs memory with FS cache data unless explictly
     requested. */
  {
    svn_cache_config_t settings = *svn_cache_config_get();
    settings.cache_size = 0;
    settings.file_handle_count = 0;
    settings.single_threaded = FALSE;
    svn_cache_config_set(&settings);
  }

#ifdef ENABLE_NLS
#ifdef WIN32
  {
    WCHAR ucs2_path[MAX_PATH];
    char *utf8_path;
    const char *internal_path;
    apr_pool_t *pool;
    apr_status_t apr_err;
    apr_size_t inwords, outbytes;
    unsigned int outlength;

    pool = svn_pool_create(g_pool);
    /* get dll name - our locale info will be in '../share/locale' */
    inwords = sizeof(ucs2_path) / sizeof(ucs2_path[0]);
    HINSTANCE moduleHandle = GetModuleHandle("libsvnjavahl-1");
    GetModuleFileNameW(moduleHandle, ucs2_path, inwords);
    inwords = lstrlenW(ucs2_path);
    outbytes = outlength = 3 * (inwords + 1);
    utf8_path = reinterpret_cast<char *>(apr_palloc(pool, outlength));
    apr_err = apr_conv_ucs2_to_utf8((const apr_wchar_t *) ucs2_path,
                                    &inwords, utf8_path, &outbytes);
    if (!apr_err && (inwords > 0 || outbytes == 0))
      apr_err = APR_INCOMPLETE;
    if (apr_err)
      {
        if (stderr)
          fprintf(stderr, "Can't convert module path to UTF-8");
        return FALSE;
      }
    utf8_path[outlength - outbytes] = '\0';
    internal_path = svn_dirent_internal_style(utf8_path, pool);
    /* get base path name */
    internal_path = svn_dirent_dirname(internal_path, pool);
    internal_path = svn_dirent_join(internal_path, SVN_LOCALE_RELATIVE_PATH,
                                  pool);
    bindtextdomain(PACKAGE_NAME, internal_path);
    svn_pool_destroy(pool);
  }
#else
  bindtextdomain(PACKAGE_NAME, SVN_LOCALE_DIR);
#endif
#endif

#if defined(WIN32) || defined(__CYGWIN__)
  /* See http://svn.apache.org/repos/asf/subversion/trunk/notes/asp-dot-net-hack.txt */
  /* ### This code really only needs to be invoked by consumers of
     ### the libsvn_wc library, which basically means SVNClient. */
  if (getenv("SVN_ASP_DOT_NET_HACK"))
    {
      err = svn_wc_set_adm_dir("_svn", g_pool);
      if (err)
        {
          if (stderr)
            {
              fprintf(stderr,
                      "%s: error: SVN_ASP_DOT_NET_HACK failed: %s\n",
                      "svnjavahl", err->message);
            }
          svn_error_clear(err);
          return FALSE;
        }
    }
#endif

  svn_error_set_malfunction_handler(svn_error_raise_on_malfunction);

  // Build all mutexes.
  g_finalizedObjectsMutex = new JNIMutex(g_pool);
  if (isExceptionThrown())
    return false;

  g_logMutex = new JNIMutex(g_pool);
  if (isExceptionThrown())
    return false;

  // initialized the thread local storage
  if (!JNIThreadData::initThreadData())
    return false;

  setEnv(env);
  if (isExceptionThrown())
    return false;

  g_initEnv = NULL;
  g_inInit = false;
  return true;
}
Exemple #18
0
/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH
   is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself,
   but create it marked with skip+skip_children.
 */
static svn_error_t *
ensure_state(struct diff_baton *eb,
             const char *local_abspath,
             svn_boolean_t recursive_skip,
             apr_pool_t *scratch_pool)
{
  struct node_state_t *ns;
  apr_pool_t *ns_pool;
  if (!eb->cur)
    {
      if (!svn_dirent_is_ancestor(eb->anchor_abspath, local_abspath))
        return SVN_NO_ERROR;

      SVN_ERR(ensure_state(eb,
                           svn_dirent_dirname(local_abspath,scratch_pool),
                           FALSE,
                           scratch_pool));
    }
  else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL))
    SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool),
                         FALSE,
                         scratch_pool));
  else
    return SVN_NO_ERROR;

  if (eb->cur && eb->cur->skip_children)
    return SVN_NO_ERROR;

  ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool);
  ns = apr_pcalloc(ns_pool, sizeof(*ns));

  ns->pool = ns_pool;
  ns->local_abspath = apr_pstrdup(ns_pool, local_abspath);
  ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath);
  ns->parent = eb->cur;
  eb->cur = ns;

  if (recursive_skip)
    {
      ns->skip = TRUE;
      ns->skip_children = TRUE;
      return SVN_NO_ERROR;
    }

  {
    svn_revnum_t revision;
    svn_error_t *err;

    err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL,
                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                                   NULL, NULL, NULL,
                                   eb->db, local_abspath,
                                   scratch_pool, scratch_pool);

    if (err)
      {
        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
          return svn_error_trace(err);
        svn_error_clear(err);

        revision = 0; /* Use original revision? */
      }
    ns->left_src = svn_diff__source_create(revision, ns->pool);
    ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool);

    SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip,
                                      &ns->skip_children,
                                      ns->relpath,
                                      ns->left_src,
                                      ns->right_src,
                                      NULL /* copyfrom_source */,
                                      ns->parent ? ns->parent->baton : NULL,
                                      eb->processor,
                                      ns->pool, scratch_pool));
  }

  return SVN_NO_ERROR;
}
Exemple #19
0
/* Implements svn_wc_status_func3_t */
static svn_error_t *
diff_status_callback(void *baton,
                     const char *local_abspath,
                     const svn_wc_status3_t *status,
                     apr_pool_t *scratch_pool)
{
  struct diff_baton *eb = baton;
  svn_wc__db_t *db = eb->db;

  if (! status->versioned)
    return SVN_NO_ERROR; /* unversioned (includes dir externals) */

  if (status->node_status == svn_wc_status_conflicted
      && status->text_status == svn_wc_status_none
      && status->prop_status == svn_wc_status_none)
    {
      /* Node is an actual only node describing a tree conflict */
      return SVN_NO_ERROR;
    }

  /* Not text/prop modified, not copied. Easy out */
  if (status->node_status == svn_wc_status_normal && !status->copied)
    return SVN_NO_ERROR;

  /* Mark all directories where we are no longer inside as closed */
  while (eb->cur
         && !svn_dirent_is_ancestor(eb->cur->local_abspath, local_abspath))
    {
      struct node_state_t *ns = eb->cur;

      if (!ns->skip)
        {
          if (ns->propchanges)
            SVN_ERR(eb->processor->dir_changed(ns->relpath,
                                               ns->left_src,
                                               ns->right_src,
                                               ns->left_props,
                                               ns->right_props,
                                               ns->propchanges,
                                               ns->baton,
                                               eb->processor,
                                               ns->pool));
          else
            SVN_ERR(eb->processor->dir_closed(ns->relpath,
                                              ns->left_src,
                                              ns->right_src,
                                              ns->baton,
                                              eb->processor,
                                              ns->pool));
        }
      eb->cur = ns->parent;
      svn_pool_clear(ns->pool);
    }
  SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool),
                       FALSE, scratch_pool));

  if (eb->cur && eb->cur->skip_children)
    return SVN_NO_ERROR;

  if (eb->changelist_hash != NULL
      && (!status->changelist
          || ! svn_hash_gets(eb->changelist_hash, status->changelist)))
    return SVN_NO_ERROR; /* Filtered via changelist */

  /* This code does about the same thing as the inner body of
     walk_local_nodes_diff() in diff_editor.c, except that
     it is already filtered by the status walker, doesn't have to
     account for remote changes (and many tiny other details) */

  {
    svn_boolean_t repos_only;
    svn_boolean_t local_only;
    svn_wc__db_status_t db_status;
    svn_boolean_t have_base;
    svn_node_kind_t base_kind;
    svn_node_kind_t db_kind = status->kind;
    svn_depth_t depth_below_here = svn_depth_unknown;

    const char *child_abspath = local_abspath;
    const char *child_relpath = svn_dirent_skip_ancestor(eb->anchor_abspath,
                                                         local_abspath);


    repos_only = FALSE;
    local_only = FALSE;

    /* ### optimize away this call using status info. Should
           be possible in almost every case (except conflict, missing, obst.)*/
    SVN_ERR(svn_wc__db_read_info(&db_status, NULL, NULL, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL,
                                 &have_base, NULL, NULL,
                                 eb->db, local_abspath,
                                 scratch_pool, scratch_pool));
    if (!have_base)
      {
        local_only = TRUE; /* Only report additions */
      }
    else if (db_status == svn_wc__db_status_normal)
      {
        /* Simple diff */
        base_kind = db_kind;
      }
    else if (db_status == svn_wc__db_status_deleted)
      {
        svn_wc__db_status_t base_status;
        repos_only = TRUE;
        SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
                                         NULL, NULL, NULL, NULL, NULL,
                                         NULL, NULL, NULL, NULL, NULL,
                                         NULL, NULL, NULL,
                                         eb->db, local_abspath,
                                         scratch_pool, scratch_pool));

        if (base_status != svn_wc__db_status_normal)
          return SVN_NO_ERROR;
      }
    else
      {
        /* working status is either added or deleted */
        svn_wc__db_status_t base_status;

        SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
                                         NULL, NULL, NULL, NULL, NULL,
                                         NULL, NULL, NULL, NULL, NULL,
                                         NULL, NULL, NULL,
                                         eb->db, local_abspath,
                                         scratch_pool, scratch_pool));

        if (base_status != svn_wc__db_status_normal)
          local_only = TRUE;
        else if (base_kind != db_kind || !eb->ignore_ancestry)
          {
            repos_only = TRUE;
            local_only = TRUE;
          }
      }

    if (repos_only)
      {
        /* Report repository form deleted */
        if (base_kind == svn_node_file)
          SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
                                              child_relpath,
                                              SVN_INVALID_REVNUM,
                                              eb->processor,
                                              eb->cur ? eb->cur->baton : NULL,
                                              scratch_pool));
        else if (base_kind == svn_node_dir)
          SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
                                             child_relpath,
                                             SVN_INVALID_REVNUM,
                                             depth_below_here,
                                             eb->processor,
                                             eb->cur ? eb->cur->baton : NULL,
                                             eb->cancel_func,
                                             eb->cancel_baton,
                                             scratch_pool));
      }
    else if (!local_only)
      {
        /* Diff base against actual */
        if (db_kind == svn_node_file)
          {
            SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath,
                                                   child_relpath,
                                                   SVN_INVALID_REVNUM,
                                                   eb->changelist_hash,
                                                   eb->processor,
                                                   eb->cur
                                                        ? eb->cur->baton
                                                        : NULL,
                                                   FALSE,
                                                   eb->cancel_func,
                                                   eb->cancel_baton,
                                                   scratch_pool));
          }
        else if (db_kind == svn_node_dir)
          {
            SVN_ERR(ensure_state(eb, local_abspath, FALSE, scratch_pool));

            if (status->prop_status != svn_wc_status_none
                && status->prop_status != svn_wc_status_normal)
              {
                apr_array_header_t *propchanges;
                SVN_ERR(svn_wc__db_base_get_props(&eb->cur->left_props,
                                                  eb->db, local_abspath,
                                                  eb->cur->pool,
                                                  scratch_pool));
                SVN_ERR(svn_wc__db_read_props(&eb->cur->right_props,
                                              eb->db, local_abspath,
                                              eb->cur->pool,
                                              scratch_pool));

                SVN_ERR(svn_prop_diffs(&propchanges,
                                       eb->cur->right_props,
                                       eb->cur->left_props,
                                       eb->cur->pool));

                eb->cur->propchanges = propchanges;
              }
          }
      }

    if (local_only && (db_status != svn_wc__db_status_deleted))
      {
        if (db_kind == svn_node_file)
          SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
                                               child_relpath,
                                               eb->processor,
                                               eb->cur ? eb->cur->baton : NULL,
                                               eb->changelist_hash,
                                               FALSE,
                                               eb->cancel_func,
                                               eb->cancel_baton,
                                               scratch_pool));
        else if (db_kind == svn_node_dir)
          SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
                                              child_relpath, depth_below_here,
                                              eb->processor,
                                              eb->cur ? eb->cur->baton : NULL,
                                              eb->changelist_hash,
                                              FALSE,
                                              eb->cancel_func,
                                              eb->cancel_baton,
                                              scratch_pool));
      }

    if (db_kind == svn_node_dir && (local_only || repos_only))
      SVN_ERR(ensure_state(eb, local_abspath, TRUE /* skip */, scratch_pool));
  }

  return SVN_NO_ERROR;
}
Exemple #20
0
static svn_error_t *
handle_external_item_change(svn_client_ctx_t *ctx,
                            const char *repos_root_url,
                            const char *parent_dir_abspath,
                            const char *parent_dir_url,
                            const char *local_abspath,
                            const char *old_defining_abspath,
                            const svn_wc_external_item2_t *new_item,
                            svn_boolean_t *timestamp_sleep,
                            apr_pool_t *scratch_pool)
{
  svn_ra_session_t *ra_session;
  svn_client__pathrev_t *new_loc;
  const char *new_url;
  svn_node_kind_t ext_kind;

  SVN_ERR_ASSERT(repos_root_url && parent_dir_url);
  SVN_ERR_ASSERT(new_item != NULL);

  /* Don't bother to check status, since we'll get that for free by
     attempting to retrieve the hash values anyway.  */

  /* When creating the absolute URL, use the pool and not the
     iterpool, since the hash table values outlive the iterpool and
     any pointers they have should also outlive the iterpool.  */

  SVN_ERR(svn_wc__resolve_relative_external_url(&new_url,
                                                new_item, repos_root_url,
                                                parent_dir_url,
                                                scratch_pool, scratch_pool));

  /* Determine if the external is a file or directory. */
  /* Get the RA connection. */
  SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc,
                                            new_url, NULL,
                                            &(new_item->peg_revision),
                                            &(new_item->revision), ctx,
                                            scratch_pool));

  SVN_ERR(svn_ra_check_path(ra_session, "", new_loc->rev, &ext_kind,
                            scratch_pool));

  if (svn_node_none == ext_kind)
    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                             _("URL '%s' at revision %ld doesn't exist"),
                             new_loc->url, new_loc->rev);

  if (svn_node_dir != ext_kind && svn_node_file != ext_kind)
    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                             _("URL '%s' at revision %ld is not a file "
                               "or a directory"),
                             new_loc->url, new_loc->rev);


  /* Not protecting against recursive externals.  Detecting them in
     the global case is hard, and it should be pretty obvious to a
     user when it happens.  Worst case: your disk fills up :-). */

  /* First notify that we're about to handle an external. */
  if (ctx->notify_func2)
    {
      (*ctx->notify_func2)(
         ctx->notify_baton2,
         svn_wc_create_notify(local_abspath,
                              svn_wc_notify_update_external,
                              scratch_pool),
         scratch_pool);
    }

  if (! old_defining_abspath)
    {
      /* The target dir might have multiple components.  Guarantee the path
         leading down to the last component. */
      SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath,
                                                             scratch_pool),
                                          scratch_pool));
    }

  switch (ext_kind)
    {
      case svn_node_dir:
        SVN_ERR(switch_dir_external(local_abspath, new_loc->url,
                                    new_item->url,
                                    &(new_item->peg_revision),
                                    &(new_item->revision),
                                    parent_dir_abspath,
                                    timestamp_sleep, ctx,
                                    scratch_pool));
        break;
      case svn_node_file:
        if (strcmp(repos_root_url, new_loc->repos_root_url))
          {
            const char *local_repos_root_url;
            const char *local_repos_uuid;
            const char *ext_repos_relpath;
            svn_error_t *err;

            /*
             * The working copy library currently requires that all files
             * in the working copy have the same repository root URL.
             * The URL from the file external's definition differs from the
             * one used by the working copy. As a workaround, replace the
             * root URL portion of the file external's URL, after making
             * sure both URLs point to the same repository. See issue #4087.
             */

            err = svn_wc__node_get_repos_info(NULL, NULL,
                                              &local_repos_root_url,
                                              &local_repos_uuid,
                                              ctx->wc_ctx, parent_dir_abspath,
                                              scratch_pool, scratch_pool);
            if (err)
              {
                if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
                    && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
                  return svn_error_trace(err);

                svn_error_clear(err);
                local_repos_root_url = NULL;
                local_repos_uuid = NULL;
              }

            ext_repos_relpath = svn_uri_skip_ancestor(new_loc->repos_root_url,
                                                      new_url, scratch_pool);
            if (local_repos_uuid == NULL || local_repos_root_url == NULL ||
                ext_repos_relpath == NULL ||
                strcmp(local_repos_uuid, new_loc->repos_uuid) != 0)
              return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                        _("Unsupported external: URL of file external '%s' "
                          "is not in repository '%s'"),
                        new_url, repos_root_url);

            new_url = svn_path_url_add_component2(local_repos_root_url,
                                                  ext_repos_relpath,
                                                  scratch_pool);
            SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc,
                                                      new_url,
                                                      NULL,
                                                      &(new_item->peg_revision),
                                                      &(new_item->revision),
                                                      ctx, scratch_pool));
          }

        SVN_ERR(switch_file_external(local_abspath,
                                     new_url,
                                     &new_item->peg_revision,
                                     &new_item->revision,
                                     parent_dir_abspath,
                                     ra_session,
                                     ctx,
                                     scratch_pool));
        break;

      default:
        SVN_ERR_MALFUNCTION();
        break;
    }

  return SVN_NO_ERROR;
}
Exemple #21
0
svn_error_t *
svn_client__handle_externals(apr_hash_t *externals_new,
                             apr_hash_t *ambient_depths,
                             const char *repos_root_url,
                             const char *target_abspath,
                             svn_depth_t requested_depth,
                             svn_boolean_t *timestamp_sleep,
                             svn_client_ctx_t *ctx,
                             apr_pool_t *scratch_pool)
{
  apr_hash_t *old_external_defs;
  apr_hash_index_t *hi;
  apr_pool_t *iterpool;

  SVN_ERR_ASSERT(repos_root_url);

  iterpool = svn_pool_create(scratch_pool);

  SVN_ERR(svn_wc__externals_defined_below(&old_external_defs,
                                          ctx->wc_ctx, target_abspath,
                                          scratch_pool, iterpool));

  for (hi = apr_hash_first(scratch_pool, externals_new);
       hi;
       hi = apr_hash_next(hi))
    {
      const char *local_abspath = svn__apr_hash_index_key(hi);
      const char *desc_text = svn__apr_hash_index_val(hi);
      svn_depth_t ambient_depth = svn_depth_infinity;

      svn_pool_clear(iterpool);

      if (ambient_depths)
        {
          const char *ambient_depth_w;

          ambient_depth_w = apr_hash_get(ambient_depths, local_abspath,
                                         svn__apr_hash_index_klen(hi));

          if (ambient_depth_w == NULL)
            {
              return svn_error_createf(
                        SVN_ERR_WC_CORRUPT, NULL,
                        _("Traversal of '%s' found no ambient depth"),
                        svn_dirent_local_style(local_abspath, scratch_pool));
            }
          else
            {
              ambient_depth = svn_depth_from_word(ambient_depth_w);
            }
        }

      SVN_ERR(handle_externals_change(ctx, repos_root_url, timestamp_sleep,
                                      local_abspath,
                                      desc_text, old_external_defs,
                                      ambient_depth, requested_depth,
                                      iterpool));
    }

  /* Remove the remaining externals */
  for (hi = apr_hash_first(scratch_pool, old_external_defs);
       hi;
       hi = apr_hash_next(hi))
    {
      const char *item_abspath = svn__apr_hash_index_key(hi);
      const char *defining_abspath = svn__apr_hash_index_val(hi);
      const char *parent_abspath;

      svn_pool_clear(iterpool);

      SVN_ERR(wrap_external_error(
                          ctx, item_abspath,
                          handle_external_item_removal(ctx, defining_abspath,
                                                       item_abspath, iterpool),
                          iterpool));

      /* Are there any unversioned directories between the removed
       * external and the DEFINING_ABSPATH which we can remove? */
      parent_abspath = item_abspath;
      do {
        svn_node_kind_t kind;

        parent_abspath = svn_dirent_dirname(parent_abspath, iterpool);
        SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, parent_abspath,
                                  FALSE /* show_deleted*/,
                                  FALSE /* show_hidden */,
                                  iterpool));
        if (kind == svn_node_none)
          {
            svn_error_t *err;

            err = svn_io_dir_remove_nonrecursive(parent_abspath, iterpool);
            if (err)
              {
                if (APR_STATUS_IS_ENOTEMPTY(err->apr_err))
                  {
                    svn_error_clear(err);
                    break; /* No parents to delete */
                  }
                else if (APR_STATUS_IS_ENOENT(err->apr_err)
                         || APR_STATUS_IS_ENOTDIR(err->apr_err))
                  {
                    svn_error_clear(err);
                    /* Fall through; parent dir might be unversioned */
                  }
                else
                  return svn_error_trace(err);
              }
          }
      } while (strcmp(parent_abspath, defining_abspath) != 0);
    }


  svn_pool_destroy(iterpool);
  return SVN_NO_ERROR;
}
Exemple #22
0
svn_error_t *
svn_client__export_externals(apr_hash_t *externals,
                             const char *from_url,
                             const char *to_abspath,
                             const char *repos_root_url,
                             svn_depth_t requested_depth,
                             const char *native_eol,
                             svn_boolean_t ignore_keywords,
                             svn_client_ctx_t *ctx,
                             apr_pool_t *scratch_pool)
{
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
  apr_pool_t *sub_iterpool = svn_pool_create(scratch_pool);
  apr_hash_index_t *hi;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath));

  for (hi = apr_hash_first(scratch_pool, externals);
       hi;
       hi = apr_hash_next(hi))
    {
      const char *local_abspath = svn__apr_hash_index_key(hi);
      const char *desc_text = svn__apr_hash_index_val(hi);
      const char *local_relpath;
      const char *dir_url;
      apr_array_header_t *items;
      int i;

      svn_pool_clear(iterpool);

      SVN_ERR(svn_wc_parse_externals_description3(&items, local_abspath,
                                                  desc_text, FALSE,
                                                  iterpool));

      if (! items->nelts)
        continue;

      local_relpath = svn_dirent_skip_ancestor(to_abspath, local_abspath);

      dir_url = svn_path_url_add_component2(from_url, local_relpath,
                                            scratch_pool);

      for (i = 0; i < items->nelts; i++)
        {
          const char *item_abspath;
          const char *new_url;
          svn_boolean_t under_root;
          svn_wc_external_item2_t *item = APR_ARRAY_IDX(items, i,
                                                svn_wc_external_item2_t *);

          svn_pool_clear(sub_iterpool);

          SVN_ERR(svn_dirent_is_under_root(&under_root, &item_abspath,
                                           local_abspath, item->target_dir,
                                           sub_iterpool));

          if (! under_root)
            {
              return svn_error_createf(
                        SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
                        _("Path '%s' is not in the working copy"),
                        svn_dirent_local_style(
                            svn_dirent_join(local_abspath, item->target_dir,
                                            sub_iterpool),
                            sub_iterpool));
            }

          SVN_ERR(svn_wc__resolve_relative_external_url(&new_url, item,
                                                        repos_root_url,
                                                        dir_url, sub_iterpool,
                                                        sub_iterpool));

          /* The target dir might have multiple components.  Guarantee
             the path leading down to the last component. */
          SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(item_abspath,
                                                                 sub_iterpool),
                                              sub_iterpool));

          SVN_ERR(wrap_external_error(
                          ctx, item_abspath,
                          svn_client_export5(NULL, new_url, item_abspath,
                                             &item->peg_revision,
                                             &item->revision,
                                             TRUE, FALSE, ignore_keywords,
                                             svn_depth_infinity,
                                             native_eol,
                                             ctx, sub_iterpool),
                          sub_iterpool));
        }
    }

  svn_pool_destroy(sub_iterpool);
  svn_pool_destroy(iterpool);

  return SVN_NO_ERROR;
}
Exemple #23
0
/* Try to update a directory external at PATH to URL at REVISION.
   Use POOL for temporary allocations, and use the client context CTX. */
static svn_error_t *
switch_dir_external(const char *local_abspath,
                    const char *url,
                    const char *url_from_externals_definition,
                    const svn_opt_revision_t *peg_revision,
                    const svn_opt_revision_t *revision,
                    const char *defining_abspath,
                    svn_boolean_t *timestamp_sleep,
                    svn_client_ctx_t *ctx,
                    apr_pool_t *pool)
{
  svn_node_kind_t kind;
  svn_error_t *err;
  svn_revnum_t external_peg_rev = SVN_INVALID_REVNUM;
  svn_revnum_t external_rev = SVN_INVALID_REVNUM;
  apr_pool_t *subpool = svn_pool_create(pool);
  const char *repos_root_url;
  const char *repos_uuid;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));

  if (peg_revision->kind == svn_opt_revision_number)
    external_peg_rev = peg_revision->value.number;

  if (revision->kind == svn_opt_revision_number)
    external_rev = revision->value.number;

  /* 
   * The code below assumes existing versioned paths are *not* part of
   * the external's defining working copy.
   * The working copy library does not support registering externals
   * on top of existing BASE nodes and will error out if we try.
   * So if the external target is part of the defining working copy's
   * BASE tree, don't attempt to create the external. Doing so would
   * leave behind a switched path instead of an external (since the
   * switch succeeds but registration of the external in the DB fails).
   * The working copy then cannot be updated until the path is switched back.
   * See issue #4085.
   */
  SVN_ERR(svn_wc__node_get_base(&kind, NULL, NULL,
                                &repos_root_url, &repos_uuid,
                                NULL, ctx->wc_ctx, local_abspath,
                                TRUE, /* ignore_enoent */
                                TRUE, /* show hidden */
                                pool, pool));
  if (kind != svn_node_unknown)
    {
      const char *wcroot_abspath;
      const char *defining_wcroot_abspath;

      SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx,
                                 local_abspath, pool, pool));
      SVN_ERR(svn_wc__get_wcroot(&defining_wcroot_abspath, ctx->wc_ctx,
                                 defining_abspath, pool, pool));
      if (strcmp(wcroot_abspath, defining_wcroot_abspath) == 0)
        return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
                                 _("The external '%s' defined in %s at '%s' "
                                   "cannot be checked out because '%s' is "
                                   "already a versioned path."),
                                   url_from_externals_definition,
                                   SVN_PROP_EXTERNALS,
                                   svn_dirent_local_style(defining_abspath,
                                                          pool),
                                   svn_dirent_local_style(local_abspath,
                                                          pool));
    }

  /* If path is a directory, try to update/switch to the correct URL
     and revision. */
  SVN_ERR(svn_io_check_path(local_abspath, &kind, pool));
  if (kind == svn_node_dir)
    {
      const char *node_url;

      /* Doubles as an "is versioned" check. */
      err = svn_wc__node_get_url(&node_url, ctx->wc_ctx, local_abspath,
                                 pool, subpool);
      if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
        {
          svn_error_clear(err);
          err = SVN_NO_ERROR;
          goto relegate;
        }
      else if (err)
        return svn_error_trace(err);

      if (node_url)
        {
          /* If we have what appears to be a version controlled
             subdir, and its top-level URL matches that of our
             externals definition, perform an update. */
          if (strcmp(node_url, url) == 0)
            {
              SVN_ERR(svn_client__update_internal(NULL, local_abspath,
                                                  revision, svn_depth_unknown,
                                                  FALSE, FALSE, FALSE, TRUE,
                                                  FALSE, TRUE,
                                                  timestamp_sleep,
                                                  ctx, subpool));

              /* We just decided that this existing directory is an external,
                 so update the external registry with this information, like
                 when checking out an external */
              SVN_ERR(svn_wc__external_register(ctx->wc_ctx,
                                    defining_abspath,
                                    local_abspath, svn_node_dir,
                                    repos_root_url, repos_uuid,
                                    svn_uri_skip_ancestor(repos_root_url,
                                                          url, pool),
                                    external_peg_rev,
                                    external_rev,
                                    pool));

              svn_pool_destroy(subpool);
              goto cleanup;
            }

          /* We'd really prefer not to have to do a brute-force
             relegation -- blowing away the current external working
             copy and checking it out anew -- so we'll first see if we
             can get away with a generally cheaper relocation (if
             required) and switch-style update.

             To do so, we need to know the repository root URL of the
             external working copy as it currently sits. */
          err = svn_wc__node_get_repos_info(NULL, NULL,
                                            &repos_root_url, &repos_uuid,
                                            ctx->wc_ctx, local_abspath,
                                            pool, subpool);
          if (err)
            {
              if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
                  && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
                return svn_error_trace(err);

              svn_error_clear(err);
              repos_root_url = NULL;
              repos_uuid = NULL;
            }

          if (repos_root_url)
            {
              /* If the new external target URL is not obviously a
                 child of the external working copy's current
                 repository root URL... */
              if (! svn_uri__is_ancestor(repos_root_url, url))
                {
                  const char *repos_root;

                  /* ... then figure out precisely which repository
                      root URL that target URL *is* a child of ... */
                  SVN_ERR(svn_client_get_repos_root(&repos_root, NULL, url,
                                                    ctx, subpool, subpool));

                  /* ... and use that to try to relocate the external
                     working copy to the target location.  */
                  err = svn_client_relocate2(local_abspath, repos_root_url,
                                             repos_root, FALSE, ctx, subpool);

                  /* If the relocation failed because the new URL
                     points to a totally different repository, we've
                     no choice but to relegate and check out a new WC. */
                  if (err
                      && (err->apr_err == SVN_ERR_WC_INVALID_RELOCATION
                          || (err->apr_err
                              == SVN_ERR_CLIENT_INVALID_RELOCATION)))
                    {
                      svn_error_clear(err);
                      goto relegate;
                    }
                  else if (err)
                    return svn_error_trace(err);

                  /* If the relocation went without a hitch, we should
                     have a new repository root URL. */
                  repos_root_url = repos_root;
                }

              SVN_ERR(svn_client__switch_internal(NULL, local_abspath, url,
                                                  peg_revision, revision,
                                                  svn_depth_infinity,
                                                  TRUE, FALSE, FALSE,
                                                  TRUE /* ignore_ancestry */,
                                                  timestamp_sleep,
                                                  ctx, subpool));

              SVN_ERR(svn_wc__external_register(ctx->wc_ctx,
                                                defining_abspath,
                                                local_abspath, svn_node_dir,
                                                repos_root_url, repos_uuid,
                                                svn_uri_skip_ancestor(
                                                            repos_root_url,
                                                            url, subpool),
                                                external_peg_rev,
                                                external_rev,
                                                subpool));

              svn_pool_destroy(subpool);
              goto cleanup;
            }
        }
    }

 relegate:

  /* Fall back on removing the WC and checking out a new one. */

  /* Ensure that we don't have any RA sessions or WC locks from failed
     operations above. */
  svn_pool_destroy(subpool);

  if (kind == svn_node_dir)
    {
      /* Buh-bye, old and busted ... */
      SVN_ERR(relegate_dir_external(ctx->wc_ctx, defining_abspath,
                                    local_abspath,
                                    ctx->cancel_func, ctx->cancel_baton,
                                    ctx->notify_func2, ctx->notify_baton2,
                                    pool));
    }
  else
    {
      /* The target dir might have multiple components.  Guarantee
         the path leading down to the last component. */
      const char *parent = svn_dirent_dirname(local_abspath, pool);
      SVN_ERR(svn_io_make_dir_recursively(parent, pool));
    }

  /* ... Hello, new hotness. */
  SVN_ERR(svn_client__checkout_internal(NULL, url, local_abspath, peg_revision,
                                        revision, svn_depth_infinity,
                                        FALSE, FALSE, timestamp_sleep,
                                        ctx, pool));

  SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL,
                                      &repos_root_url,
                                      &repos_uuid,
                                      ctx->wc_ctx, local_abspath,
                                      pool, pool));

  SVN_ERR(svn_wc__external_register(ctx->wc_ctx,
                                    defining_abspath,
                                    local_abspath, svn_node_dir,
                                    repos_root_url, repos_uuid,
                                    svn_uri_skip_ancestor(repos_root_url,
                                                          url, pool),
                                    external_peg_rev,
                                    external_rev,
                                    pool));

 cleanup:
  /* Issues #4123 and #4130: We don't need to keep the newly checked
     out external's DB open. */
  SVN_ERR(svn_wc__close_db(local_abspath, ctx->wc_ctx, pool));

  return SVN_NO_ERROR;
}
Exemple #24
0
/* Given a list of committables described by their common base abspath
   BASE_ABSPATH and a list of relative dirents TARGET_RELPATHS determine
   which absolute paths must be locked to commit all these targets and
   return this as a const char * array in LOCK_TARGETS

   Allocate the result in RESULT_POOL and use SCRATCH_POOL for temporary
   storage */
static svn_error_t *
determine_lock_targets(apr_array_header_t **lock_targets,
                       svn_wc_context_t *wc_ctx,
                       const char *base_abspath,
                       const apr_array_header_t *target_relpaths,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool)
{
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
  apr_hash_t *wc_items; /* const char *wcroot -> apr_array_header_t */
  apr_hash_index_t *hi;
  int i;

  wc_items = apr_hash_make(scratch_pool);

  /* Create an array of targets for each working copy used */
  for (i = 0; i < target_relpaths->nelts; i++)
    {
      const char *target_abspath;
      const char *wcroot_abspath;
      apr_array_header_t *wc_targets;
      svn_error_t *err;
      const char *target_relpath = APR_ARRAY_IDX(target_relpaths, i,
                                                 const char *);

      svn_pool_clear(iterpool);
      target_abspath = svn_dirent_join(base_abspath, target_relpath,
                                       scratch_pool);

      err = svn_wc__get_wcroot(&wcroot_abspath, wc_ctx, target_abspath,
                               iterpool, iterpool);

      if (err)
        {
          if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
            {
              svn_error_clear(err);
              continue;
            }
          return svn_error_trace(err);
        }

      wc_targets = svn_hash_gets(wc_items, wcroot_abspath);

      if (! wc_targets)
        {
          wc_targets = apr_array_make(scratch_pool, 4, sizeof(const char *));
          svn_hash_sets(wc_items, apr_pstrdup(scratch_pool, wcroot_abspath),
                        wc_targets);
        }

      APR_ARRAY_PUSH(wc_targets, const char *) = target_abspath;
    }

  *lock_targets = apr_array_make(result_pool, apr_hash_count(wc_items),
                                 sizeof(const char *));

  /* For each working copy determine where to lock */
  for (hi = apr_hash_first(scratch_pool, wc_items);
       hi;
       hi = apr_hash_next(hi))
    {
      const char *common;
      const char *wcroot_abspath = svn__apr_hash_index_key(hi);
      apr_array_header_t *wc_targets = svn__apr_hash_index_val(hi);

      svn_pool_clear(iterpool);

      if (wc_targets->nelts == 1)
        {
          const char *target_abspath;
          target_abspath = APR_ARRAY_IDX(wc_targets, 0, const char *);

          if (! strcmp(wcroot_abspath, target_abspath))
            {
              APR_ARRAY_PUSH(*lock_targets, const char *)
                      = apr_pstrdup(result_pool, target_abspath);
            }
          else
            {
              /* Lock the parent to allow deleting the target */
              APR_ARRAY_PUSH(*lock_targets, const char *)
                      = svn_dirent_dirname(target_abspath, result_pool);
            }
        }
Exemple #25
0
svn_error_t *
svn_client__update_internal(svn_revnum_t *result_rev,
                            const char *local_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 make_parents,
                            svn_boolean_t innerupdate,
                            svn_boolean_t *timestamp_sleep,
                            svn_client_ctx_t *ctx,
                            apr_pool_t *pool)
{
  const char *anchor_abspath, *lockroot_abspath;
  svn_error_t *err;
  svn_opt_revision_t peg_revision = *revision;
  apr_hash_t *conflicted_paths
    = ctx->conflict_func2 ? apr_hash_make(pool) : NULL;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
  SVN_ERR_ASSERT(! (innerupdate && make_parents));

  if (make_parents)
    {
      int i;
      const char *parent_abspath = local_abspath;
      apr_array_header_t *missing_parents =
        apr_array_make(pool, 4, sizeof(const char *));

      while (1)
        {
          /* Try to lock.  If we can't lock because our target (or its
             parent) isn't a working copy, we'll try to walk up the
             tree to find a working copy, remembering this path's
             parent as one we need to flesh out.  */
          err = svn_wc__acquire_write_lock(&lockroot_abspath, ctx->wc_ctx,
                                           parent_abspath, !innerupdate,
                                           pool, pool);
          if (!err)
            break;
          if ((err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
              || svn_dirent_is_root(parent_abspath, strlen(parent_abspath)))
            return err;
          svn_error_clear(err);

          /* Remember the parent of our update target as a missing
             parent. */
          parent_abspath = svn_dirent_dirname(parent_abspath, pool);
          APR_ARRAY_PUSH(missing_parents, const char *) = parent_abspath;
        }

      /* Run 'svn up --depth=empty' (effectively) on the missing
         parents, if any. */
      anchor_abspath = lockroot_abspath;
      for (i = missing_parents->nelts - 1; i >= 0; i--)
        {
          const char *missing_parent =
            APR_ARRAY_IDX(missing_parents, i, const char *);

          err = update_internal(result_rev, conflicted_paths,
                                missing_parent, anchor_abspath,
                                &peg_revision, svn_depth_empty, FALSE,
                                ignore_externals, allow_unver_obstructions,
                                adds_as_modification, timestamp_sleep,
                                FALSE, ctx, pool);
          if (err)
            goto cleanup;
          anchor_abspath = missing_parent;

          /* If we successfully updated a missing parent, let's re-use
             the returned revision number for future updates for the
             sake of consistency. */
          peg_revision.kind = svn_opt_revision_number;
          peg_revision.value.number = *result_rev;
        }
    }
  else
    {
Exemple #26
0
svn_error_t *
svn_client_status6(svn_revnum_t *result_rev,
                   svn_client_ctx_t *ctx,
                   const char *path,
                   const svn_opt_revision_t *revision,
                   svn_depth_t depth,
                   svn_boolean_t get_all,
                   svn_boolean_t check_out_of_date,
                   svn_boolean_t check_working_copy,
                   svn_boolean_t no_ignore,
                   svn_boolean_t ignore_externals,
                   svn_boolean_t depth_as_sticky,
                   const apr_array_header_t *changelists,
                   svn_client_status_func_t status_func,
                   void *status_baton,
                   apr_pool_t *pool)  /* ### aka scratch_pool */
{
    struct status_baton sb;
    const char *dir, *dir_abspath;
    const char *target_abspath;
    const char *target_basename;
    apr_array_header_t *ignores;
    svn_error_t *err;
    apr_hash_t *changelist_hash = NULL;

    /* Override invalid combinations of the check_out_of_date and
       check_working_copy flags. */
    if (!check_out_of_date)
        check_working_copy = TRUE;

    if (svn_path_is_url(path))
        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
                                 _("'%s' is not a local path"), path);

    if (changelists && changelists->nelts)
        SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool));

    if (result_rev)
        *result_rev = SVN_INVALID_REVNUM;

    sb.real_status_func = status_func;
    sb.real_status_baton = status_baton;
    sb.deleted_in_repos = FALSE;
    sb.changelist_hash = changelist_hash;
    sb.wc_ctx = ctx->wc_ctx;

    SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool));

    if (check_out_of_date)
    {
        /* The status editor only works on directories, so get the ancestor
           if necessary */

        svn_node_kind_t kind;

        SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath,
                                  TRUE, FALSE, pool));

        /* Dir must be a working copy directory or the status editor fails */
        if (kind == svn_node_dir)
        {
            dir_abspath = target_abspath;
            target_basename = "";
            dir = path;
        }
        else
        {
            dir_abspath = svn_dirent_dirname(target_abspath, pool);
            target_basename = svn_dirent_basename(target_abspath, NULL);
            dir = svn_dirent_dirname(path, pool);

            if (kind == svn_node_file)
            {
                if (depth == svn_depth_empty)
                    depth = svn_depth_files;
            }
            else
            {
                err = svn_wc_read_kind2(&kind, ctx->wc_ctx, dir_abspath,
                                        FALSE, FALSE, pool);

                svn_error_clear(err);

                if (err || kind != svn_node_dir)
                {
                    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
                                             _("'%s' is not a working copy"),
                                             svn_dirent_local_style(path, pool));
                }
            }
        }
    }
    else
    {
        dir = path;
        dir_abspath = target_abspath;
    }

    if (svn_dirent_is_absolute(dir))
    {
        sb.anchor_abspath = NULL;
        sb.anchor_relpath = NULL;
    }
    else
    {
        sb.anchor_abspath = dir_abspath;
        sb.anchor_relpath = dir;
    }

    /* Get the status edit, and use our wrapping status function/baton
       as the callback pair. */
    SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool));

    /* If we want to know about out-of-dateness, we crawl the working copy and
       let the RA layer drive the editor for real.  Otherwise, we just close the
       edit.  :-) */
    if (check_out_of_date)
    {
        svn_ra_session_t *ra_session;
        const char *URL;
        svn_node_kind_t kind;
        svn_boolean_t server_supports_depth;
        const svn_delta_editor_t *editor;
        void *edit_baton, *set_locks_baton;
        svn_revnum_t edit_revision = SVN_INVALID_REVNUM;

        /* Get full URL from the ANCHOR. */
        SVN_ERR(svn_client_url_from_path2(&URL, dir_abspath, ctx,
                                          pool, pool));

        if (!URL)
            return svn_error_createf
                   (SVN_ERR_ENTRY_MISSING_URL, NULL,
                    _("Entry '%s' has no URL"),
                    svn_dirent_local_style(dir, pool));

        /* Open a repository session to the URL. */
        SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL,
                dir_abspath, NULL,
                FALSE, TRUE,
                ctx, pool, pool));

        SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
                                      SVN_RA_CAPABILITY_DEPTH, pool));

        SVN_ERR(svn_wc__get_status_editor(&editor, &edit_baton, &set_locks_baton,
                                          &edit_revision, ctx->wc_ctx,
                                          dir_abspath, target_basename,
                                          depth, get_all, check_working_copy,
                                          no_ignore, depth_as_sticky,
                                          server_supports_depth,
                                          ignores, tweak_status, &sb,
                                          ctx->cancel_func, ctx->cancel_baton,
                                          pool, pool));


        /* Verify that URL exists in HEAD.  If it doesn't, this can save
           us a whole lot of hassle; if it does, the cost of this
           request should be minimal compared to the size of getting
           back the average amount of "out-of-date" information. */
        SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM,
                                  &kind, pool));
        if (kind == svn_node_none)
        {
            svn_boolean_t added;

            /* Our status target does not exist in HEAD.  If we've got
               it locally added, that's okay.  But if it was previously
               versioned, then it must have since been deleted from the
               repository.  (Note that "locally replaced" doesn't count
               as "added" in this case.)  */
            SVN_ERR(svn_wc__node_is_added(&added, ctx->wc_ctx,
                                          dir_abspath, pool));
            if (! added)
                sb.deleted_in_repos = TRUE;

            /* And now close the edit. */
            SVN_ERR(editor->close_edit(edit_baton, pool));
        }
        else
        {
            svn_revnum_t revnum;
            report_baton_t rb;
            svn_depth_t status_depth;

            if (revision->kind == svn_opt_revision_head)
            {
                /* Cause the revision number to be omitted from the request,
                   which implies HEAD. */
                revnum = SVN_INVALID_REVNUM;
            }
            else
            {
                /* Get a revision number for our status operation. */
                SVN_ERR(svn_client__get_revision_number(&revnum, NULL,
                                                        ctx->wc_ctx,
                                                        target_abspath,
                                                        ra_session, revision,
                                                        pool));
            }

            if (depth_as_sticky || !server_supports_depth)
                status_depth = depth;
            else
                status_depth = svn_depth_unknown; /* Use depth from WC */

            /* Do the deed.  Let the RA layer drive the status editor. */
            SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter,
                                      &rb.wrapped_report_baton,
                                      target_basename, revnum, status_depth,
                                      editor, edit_baton, pool));

            /* Init the report baton. */
            rb.ancestor = apr_pstrdup(pool, URL); /* Edited later */
            rb.set_locks_baton = set_locks_baton;
            rb.ctx = ctx;
            rb.pool = pool;

            if (depth == svn_depth_unknown)
                rb.depth = svn_depth_infinity;
            else
                rb.depth = depth;

            /* Drive the reporter structure, describing the revisions
               within PATH.  When we call reporter->finish_report,
               EDITOR will be driven to describe differences between our
               working copy and HEAD. */
            SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx,
                                            target_abspath,
                                            &lock_fetch_reporter, &rb,
                                            FALSE /* restore_files */,
                                            depth, (! depth_as_sticky),
                                            (! server_supports_depth),
                                            FALSE /* use_commit_times */,
                                            ctx->cancel_func, ctx->cancel_baton,
                                            NULL, NULL, pool));
        }

        if (ctx->notify_func2)
        {
            svn_wc_notify_t *notify
                = svn_wc_create_notify(target_abspath,
                                       svn_wc_notify_status_completed, pool);
            notify->revision = edit_revision;
            ctx->notify_func2(ctx->notify_baton2, notify, pool);
        }

        /* If the caller wants the result revision, give it to them. */
        if (result_rev)
            *result_rev = edit_revision;
    }
    else
    {
        err = svn_wc_walk_status(ctx->wc_ctx, target_abspath,
                                 depth, get_all, no_ignore, FALSE, ignores,
                                 tweak_status, &sb,
                                 ctx->cancel_func, ctx->cancel_baton,
                                 pool);

        if (err && err->apr_err == SVN_ERR_WC_MISSING)
        {
            /* This error code is checked for in svn to continue after
               this error */
            svn_error_clear(err);
            return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
                                     _("'%s' is not a working copy"),
                                     svn_dirent_local_style(path, pool));
        }

        SVN_ERR(err);
    }

    /* We only descend into an external if depth is svn_depth_infinity or
       svn_depth_unknown.  However, there are conceivable behaviors that
       would involve descending under other circumstances; thus, we pass
       depth anyway, so the code will DTRT if we change the conditional
       in the future.
    */
    if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
    {
        apr_hash_t *external_map;
        SVN_ERR(svn_wc__externals_defined_below(&external_map,
                                                ctx->wc_ctx, target_abspath,
                                                pool, pool));


        SVN_ERR(do_external_status(ctx, external_map,
                                   depth, get_all,
                                   check_out_of_date, check_working_copy,
                                   no_ignore, changelists,
                                   sb.anchor_abspath, sb.anchor_relpath,
                                   status_func, status_baton, pool));
    }

    return SVN_NO_ERROR;
}
Exemple #27
0
/* Make an unversioned copy of the versioned file at FROM_ABSPATH.  Copy it
 * to the destination path TO_ABSPATH.
 *
 * If REVISION is svn_opt_revision_working, copy the working version,
 * otherwise copy the base version.
 *
 * Expand the file's keywords according to the source file's 'svn:keywords'
 * property, if present.  If copying a locally modified working version,
 * append 'M' to the revision number and use '(local)' for the author.
 *
 * Translate the file's line endings according to the source file's
 * 'svn:eol-style' property, if present.  If NATIVE_EOL is not NULL, use it
 * in place of the native EOL style.  Throw an error if the source file has
 * inconsistent line endings and EOL translation is attempted.
 *
 * Set the destination file's modification time to the source file's
 * modification time if copying the working version and the working version
 * is locally modified; otherwise set it to the versioned file's last
 * changed time.
 *
 * Set the destination file's 'executable' flag according to the source
 * file's 'svn:executable' property.
 */
static svn_error_t *
copy_one_versioned_file(const char *from_abspath,
                        const char *to_abspath,
                        svn_client_ctx_t *ctx,
                        const svn_opt_revision_t *revision,
                        const char *native_eol,
                        svn_boolean_t ignore_keywords,
                        apr_pool_t *scratch_pool)
{
  apr_hash_t *kw = NULL;
  svn_subst_eol_style_t style;
  apr_hash_t *props;
  svn_string_t *eol_style, *keywords, *executable, *special;
  const char *eol = NULL;
  svn_boolean_t local_mod = FALSE;
  apr_time_t tm;
  svn_stream_t *source;
  svn_stream_t *dst_stream;
  const char *dst_tmp;
  svn_error_t *err;
  svn_boolean_t is_deleted;
  svn_wc_context_t *wc_ctx = ctx->wc_ctx;

  SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, wc_ctx, from_abspath,
                                         scratch_pool));

  /* Don't export 'deleted' files and directories unless it's a
     revision other than WORKING.  These files and directories
     don't really exist in WORKING. */
  if (revision->kind == svn_opt_revision_working && is_deleted)
    return SVN_NO_ERROR;

  if (revision->kind != svn_opt_revision_working)
    {
      /* Only export 'added' files when the revision is WORKING. This is not
         WORKING, so skip the 'added' files, since they didn't exist
         in the BASE revision and don't have an associated text-base.

         'replaced' files are technically the same as 'added' files.
         ### TODO: Handle replaced nodes properly.
         ###       svn_opt_revision_base refers to the "new"
         ###       base of the node. That means, if a node is locally
         ###       replaced, export skips this node, as if it was locally
         ###       added, because svn_opt_revision_base refers to the base
         ###       of the added node, not to the node that was deleted.
         ###       In contrast, when the node is copied-here or moved-here,
         ###       the copy/move source's content will be exported.
         ###       It is currently not possible to export the revert-base
         ###       when a node is locally replaced. We need a new
         ###       svn_opt_revision_ enum value for proper distinction
         ###       between revert-base and commit-base.

         Copied-/moved-here nodes have a base, so export both added and
         replaced files when they involve a copy-/move-here.

         We get all this for free from evaluating SOURCE == NULL:
       */
      SVN_ERR(svn_wc_get_pristine_contents2(&source, wc_ctx, from_abspath,
                                            scratch_pool, scratch_pool));
      if (source == NULL)
        return SVN_NO_ERROR;

      SVN_ERR(svn_wc_get_pristine_props(&props, wc_ctx, from_abspath,
                                        scratch_pool, scratch_pool));
    }
  else
    {
      svn_wc_status3_t *status;

      /* ### hmm. this isn't always a specialfile. this will simply open
         ### the file readonly if it is a regular file. */
      SVN_ERR(svn_subst_read_specialfile(&source, from_abspath, scratch_pool,
                                         scratch_pool));

      SVN_ERR(svn_wc_prop_list2(&props, wc_ctx, from_abspath, scratch_pool,
                                scratch_pool));
      SVN_ERR(svn_wc_status3(&status, wc_ctx, from_abspath, scratch_pool,
                             scratch_pool));
      if (status->text_status != svn_wc_status_normal)
        local_mod = TRUE;
    }

  /* We can early-exit if we're creating a special file. */
  special = apr_hash_get(props, SVN_PROP_SPECIAL,
                         APR_HASH_KEY_STRING);
  if (special != NULL)
    {
      /* Create the destination as a special file, and copy the source
         details into the destination stream. */
      SVN_ERR(svn_subst_create_specialfile(&dst_stream, to_abspath,
                                           scratch_pool, scratch_pool));
      return svn_error_trace(
        svn_stream_copy3(source, dst_stream, NULL, NULL, scratch_pool));
    }


  eol_style = apr_hash_get(props, SVN_PROP_EOL_STYLE,
                           APR_HASH_KEY_STRING);
  keywords = apr_hash_get(props, SVN_PROP_KEYWORDS,
                          APR_HASH_KEY_STRING);
  executable = apr_hash_get(props, SVN_PROP_EXECUTABLE,
                            APR_HASH_KEY_STRING);

  if (eol_style)
    SVN_ERR(get_eol_style(&style, &eol, eol_style->data, native_eol));

  if (local_mod)
    {
      /* Use the modified time from the working copy of
         the file */
      SVN_ERR(svn_io_file_affected_time(&tm, from_abspath, scratch_pool));
    }
  else
    {
      SVN_ERR(svn_wc__node_get_changed_info(NULL, &tm, NULL, wc_ctx,
                                            from_abspath, scratch_pool,
                                            scratch_pool));
    }

  if (keywords)
    {
      svn_revnum_t changed_rev;
      const char *suffix;
      const char *url;
      const char *author;

      SVN_ERR(svn_wc__node_get_changed_info(&changed_rev, NULL, &author,
                                            wc_ctx, from_abspath, scratch_pool,
                                            scratch_pool));

      if (local_mod)
        {
          /* For locally modified files, we'll append an 'M'
             to the revision number, and set the author to
             "(local)" since we can't always determine the
             current user's username */
          suffix = "M";
          author = _("(local)");
        }
      else
        {
          suffix = "";
        }

      SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, from_abspath,
                                   scratch_pool, scratch_pool));

      SVN_ERR(svn_subst_build_keywords2
              (&kw, keywords->data,
               apr_psprintf(scratch_pool, "%ld%s", changed_rev, suffix),
               url, tm, author, scratch_pool));
    }

  /* For atomicity, we translate to a tmp file and then rename the tmp file
     over the real destination. */
  SVN_ERR(svn_stream_open_unique(&dst_stream, &dst_tmp,
                                 svn_dirent_dirname(to_abspath, scratch_pool),
                                 svn_io_file_del_none, scratch_pool,
                                 scratch_pool));

  /* If some translation is needed, then wrap the output stream (this is
     more efficient than wrapping the input). */
  if (eol || (kw && (apr_hash_count(kw) > 0)))
    dst_stream = svn_subst_stream_translated(dst_stream,
                                             eol,
                                             FALSE /* repair */,
                                             kw,
                                             ! ignore_keywords /* expand */,
                                             scratch_pool);

  /* ###: use cancel func/baton in place of NULL/NULL below. */
  err = svn_stream_copy3(source, dst_stream, NULL, NULL, scratch_pool);

  if (!err && executable)
    err = svn_io_set_file_executable(dst_tmp, TRUE, FALSE, scratch_pool);

  if (!err)
    err = svn_io_set_file_affected_time(tm, dst_tmp, scratch_pool);

  if (err)
    return svn_error_compose_create(err, svn_io_remove_file2(dst_tmp, FALSE,
                                                             scratch_pool));

  /* Now that dst_tmp contains the translated data, do the atomic rename. */
  SVN_ERR(svn_io_file_rename(dst_tmp, to_abspath, scratch_pool));

  if (ctx->notify_func2)
    {
      svn_wc_notify_t *notify = svn_wc_create_notify(to_abspath,
                                      svn_wc_notify_update_add, scratch_pool);
      notify->kind = svn_node_file;
      (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
    }

  return SVN_NO_ERROR;
}
/* Write to DIGEST_PATH a representation of CHILDREN (which may be
   empty, if the versioned path in FS represented by DIGEST_PATH has
   no children) and LOCK (which may be NULL if that versioned path is
   lock itself locked).  Set the permissions of DIGEST_PATH to those of
   PERMS_REFERENCE.  Use POOL for all allocations.
 */
static svn_error_t *
write_digest_file(apr_hash_t *children,
                  svn_lock_t *lock,
                  const char *fs_path,
                  const char *digest_path,
                  const char *perms_reference,
                  apr_pool_t *pool)
{
  svn_error_t *err = SVN_NO_ERROR;
  svn_stream_t *stream;
  apr_hash_index_t *hi;
  apr_hash_t *hash = apr_hash_make(pool);
  const char *tmp_path;

  SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_dirent_join(fs_path, PATH_LOCKS_DIR,
                                                       pool), fs_path, pool));
  SVN_ERR(svn_fs_fs__ensure_dir_exists(svn_dirent_dirname(digest_path, pool),
                                       fs_path, pool));

  if (lock)
    {
      const char *creation_date = NULL, *expiration_date = NULL;
      if (lock->creation_date)
        creation_date = svn_time_to_cstring(lock->creation_date, pool);
      if (lock->expiration_date)
        expiration_date = svn_time_to_cstring(lock->expiration_date, pool);
      hash_store(hash, PATH_KEY, sizeof(PATH_KEY)-1,
                 lock->path, APR_HASH_KEY_STRING, pool);
      hash_store(hash, TOKEN_KEY, sizeof(TOKEN_KEY)-1,
                 lock->token, APR_HASH_KEY_STRING, pool);
      hash_store(hash, OWNER_KEY, sizeof(OWNER_KEY)-1,
                 lock->owner, APR_HASH_KEY_STRING, pool);
      hash_store(hash, COMMENT_KEY, sizeof(COMMENT_KEY)-1,
                 lock->comment, APR_HASH_KEY_STRING, pool);
      hash_store(hash, IS_DAV_COMMENT_KEY, sizeof(IS_DAV_COMMENT_KEY)-1,
                 lock->is_dav_comment ? "1" : "0", 1, pool);
      hash_store(hash, CREATION_DATE_KEY, sizeof(CREATION_DATE_KEY)-1,
                 creation_date, APR_HASH_KEY_STRING, pool);
      hash_store(hash, EXPIRATION_DATE_KEY, sizeof(EXPIRATION_DATE_KEY)-1,
                 expiration_date, APR_HASH_KEY_STRING, pool);
    }
  if (apr_hash_count(children))
    {
      svn_stringbuf_t *children_list = svn_stringbuf_create("", pool);
      for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi))
        {
          svn_stringbuf_appendbytes(children_list,
                                    svn__apr_hash_index_key(hi),
                                    svn__apr_hash_index_klen(hi));
          svn_stringbuf_appendbyte(children_list, '\n');
        }
      hash_store(hash, CHILDREN_KEY, sizeof(CHILDREN_KEY)-1,
                 children_list->data, children_list->len, pool);
    }

  SVN_ERR(svn_stream_open_unique(&stream, &tmp_path,
                                 svn_dirent_dirname(digest_path, pool),
                                 svn_io_file_del_none, pool, pool));
  if ((err = svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool)))
    {
      svn_error_clear(svn_stream_close(stream));
      return svn_error_createf(err->apr_err,
                               err,
                               _("Cannot write lock/entries hashfile '%s'"),
                               svn_dirent_local_style(tmp_path, pool));
    }

  SVN_ERR(svn_stream_close(stream));
  SVN_ERR(svn_io_file_rename(tmp_path, digest_path, pool));
  SVN_ERR(svn_io_copy_perms(perms_reference, digest_path, pool));
  return SVN_NO_ERROR;
}
Exemple #29
0
/* Make an unversioned copy of the versioned file or directory tree at the
 * source path FROM_ABSPATH.  Copy it to the destination path TO_ABSPATH.
 *
 * If REVISION is svn_opt_revision_working, copy the working version,
 * otherwise copy the base version.
 *
 * See copy_one_versioned_file() for details of file copying behaviour,
 * including IGNORE_KEYWORDS and NATIVE_EOL.
 *
 * Include externals unless IGNORE_EXTERNALS is true.
 *
 * Recurse according to DEPTH.
 *

 */
static svn_error_t *
copy_versioned_files(const char *from_abspath,
                     const char *to_abspath,
                     const svn_opt_revision_t *revision,
                     svn_boolean_t force,
                     svn_boolean_t ignore_externals,
                     svn_boolean_t ignore_keywords,
                     svn_depth_t depth,
                     const char *native_eol,
                     svn_client_ctx_t *ctx,
                     apr_pool_t *pool)
{
  svn_error_t *err;
  apr_pool_t *iterpool;
  const apr_array_header_t *children;
  svn_node_kind_t from_kind;
  svn_depth_t node_depth;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(from_abspath));
  SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath));

  /* Only export 'added' and 'replaced' files when the revision is WORKING;
     when the revision is BASE (i.e. != WORKING), only export 'added' and
     'replaced' files when they are part of a copy-/move-here. Otherwise, skip
     them, since they don't have an associated text-base. This condition for
     added/replaced simply is an optimization. Added and replaced files would
     be handled similarly by svn_wc_get_pristine_contents2(), which would
     return NULL if they have no base associated.
     TODO: We may prefer not to duplicate this condition and rather use
     svn_wc_get_pristine_contents2() or a dedicated new function instead.

     Don't export 'deleted' files and directories unless it's a
     revision other than WORKING.  These files and directories
     don't really exist in WORKING. */
  if (revision->kind != svn_opt_revision_working)
    {
      svn_boolean_t is_added;
      const char *repos_relpath;

      SVN_ERR(svn_wc__node_get_origin(&is_added, NULL, &repos_relpath,
                                      NULL, NULL, NULL,
                                      ctx->wc_ctx, from_abspath, FALSE,
                                      pool, pool));

      if (is_added && !repos_relpath)
        return SVN_NO_ERROR; /* Local addition */
    }
  else
    {
      svn_boolean_t is_deleted;

      SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, ctx->wc_ctx,
                                             from_abspath, pool));
      if (is_deleted)
        return SVN_NO_ERROR;
    }

  SVN_ERR(svn_wc_read_kind(&from_kind, ctx->wc_ctx, from_abspath, FALSE,
                           pool));

  if (from_kind == svn_node_dir)
    {
      apr_fileperms_t perm = APR_OS_DEFAULT;
      int j;

      /* Try to make the new directory.  If this fails because the
         directory already exists, check our FORCE flag to see if we
         care. */

      /* Keep the source directory's permissions if applicable.
         Skip retrieving the umask on windows. Apr does not implement setting
         filesystem privileges on Windows.
         Retrieving the file permissions with APR_FINFO_PROT | APR_FINFO_OWNER
         is documented to be 'incredibly expensive' */
#ifndef WIN32
      if (revision->kind == svn_opt_revision_working)
        {
          apr_finfo_t finfo;
          SVN_ERR(svn_io_stat(&finfo, from_abspath, APR_FINFO_PROT, pool));
          perm = finfo.protection;
        }
#endif
      err = svn_io_dir_make(to_abspath, perm, pool);
      if (err)
        {
          if (! APR_STATUS_IS_EEXIST(err->apr_err))
            return svn_error_trace(err);
          if (! force)
            SVN_ERR_W(err, _("Destination directory exists, and will not be "
                             "overwritten unless forced"));
          else
            svn_error_clear(err);
        }

      SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx, from_abspath,
                                        FALSE, pool, pool));

      iterpool = svn_pool_create(pool);
      for (j = 0; j < children->nelts; j++)
        {
          const char *child_abspath = APR_ARRAY_IDX(children, j, const char *);
          const char *child_name = svn_dirent_basename(child_abspath, NULL);
          const char *target_abspath;
          svn_node_kind_t child_kind;

          svn_pool_clear(iterpool);

          if (ctx->cancel_func)
            SVN_ERR(ctx->cancel_func(ctx->cancel_baton));

          target_abspath = svn_dirent_join(to_abspath, child_name, iterpool);

          SVN_ERR(svn_wc_read_kind(&child_kind, ctx->wc_ctx, child_abspath,
                                   FALSE, iterpool));

          if (child_kind == svn_node_dir)
            {
              if (depth == svn_depth_infinity
                  || depth == svn_depth_immediates)
                {
                  if (ctx->notify_func2)
                    {
                      svn_wc_notify_t *notify =
                          svn_wc_create_notify(target_abspath,
                                               svn_wc_notify_update_add, pool);
                      notify->kind = svn_node_dir;
                      (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
                    }

                  if (depth == svn_depth_infinity)
                    SVN_ERR(copy_versioned_files(child_abspath, target_abspath,
                                                 revision, force,
                                                 ignore_externals,
                                                 ignore_keywords, depth,
                                                 native_eol, ctx, iterpool));
                  else
                    SVN_ERR(svn_io_make_dir_recursively(target_abspath,
                                                        iterpool));
                }
            }
          else if (child_kind == svn_node_file
                   && depth >= svn_depth_files)
            {
              svn_node_kind_t external_kind;

              SVN_ERR(svn_wc__read_external_info(&external_kind,
                                                 NULL, NULL, NULL,
                                                 NULL, ctx->wc_ctx,
                                                 child_abspath,
                                                 child_abspath, TRUE,
                                                 pool, pool));

              if (external_kind != svn_node_file)
                SVN_ERR(copy_one_versioned_file(child_abspath, target_abspath,
                                                ctx, revision,
                                                native_eol, ignore_keywords,
                                                iterpool));
            }
        }

      SVN_ERR(svn_wc__node_get_depth(&node_depth, ctx->wc_ctx,
                                     from_abspath, pool));

      /* Handle externals. */
      if (! ignore_externals && depth == svn_depth_infinity
          && node_depth == svn_depth_infinity)
        {
          apr_array_header_t *ext_items;
          const svn_string_t *prop_val;

          SVN_ERR(svn_wc_prop_get2(&prop_val, ctx->wc_ctx, from_abspath,
                                   SVN_PROP_EXTERNALS, pool, pool));
          if (prop_val != NULL)
            {
              int i;

              SVN_ERR(svn_wc_parse_externals_description3(&ext_items,
                                                          from_abspath,
                                                          prop_val->data,
                                                          FALSE, pool));
              for (i = 0; i < ext_items->nelts; ++i)
                {
                  svn_wc_external_item2_t *ext_item;
                  const char *new_from, *new_to;

                  svn_pool_clear(iterpool);

                  ext_item = APR_ARRAY_IDX(ext_items, i,
                                           svn_wc_external_item2_t *);
                  new_from = svn_dirent_join(from_abspath,
                                             ext_item->target_dir,
                                             iterpool);
                  new_to = svn_dirent_join(to_abspath, ext_item->target_dir,
                                           iterpool);

                   /* The target dir might have parents that don't exist.
                      Guarantee the path upto the last component. */
                  if (!svn_dirent_is_root(ext_item->target_dir,
                                          strlen(ext_item->target_dir)))
                    {
                      const char *parent = svn_dirent_dirname(new_to, iterpool);
                      SVN_ERR(svn_io_make_dir_recursively(parent, iterpool));
                    }

                  SVN_ERR(copy_versioned_files(new_from, new_to,
                                               revision, force, FALSE,
                                               ignore_keywords,
                                               svn_depth_infinity, native_eol,
                                               ctx, iterpool));
                }
            }
Exemple #30
0
/* ...

   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;
}