Exemplo n.º 1
0
/* Create an empty repository and WC for the test TEST_NAME.  Set *REPOS_URL
 * to the URL of the new repository and *WC_ABSPATH to the root path of the
 * new WC.
 *
 * Create the repository and WC in subdirectories called
 * REPOSITORIES_WORK_DIR/TEST_NAME and WCS_WORK_DIR/TEST_NAME respectively,
 * within the current working directory.
 *
 * Register the repo and WC to be cleaned up when the test suite exits. */
static svn_error_t *
create_repos_and_wc(const char **repos_url,
                    const char **wc_abspath,
                    const char *test_name,
                    const svn_test_opts_t *opts,
                    apr_pool_t *pool)
{
    const char *repos_path = svn_relpath_join(REPOSITORIES_WORK_DIR, test_name,
                             pool);
    const char *wc_path = svn_relpath_join(WCS_WORK_DIR, test_name, pool);

    /* Remove the repo and WC dirs if they already exist, to ensure the test
     * will run even if a previous failed attempt was not cleaned up. */
    SVN_ERR(svn_io_remove_dir2(repos_path, TRUE, NULL, NULL, pool));
    SVN_ERR(svn_io_remove_dir2(wc_path, TRUE, NULL, NULL, pool));

    /* Create the parent dirs of the repo and WC if necessary. */
    SVN_ERR(svn_io_make_dir_recursively(REPOSITORIES_WORK_DIR, pool));
    SVN_ERR(svn_io_make_dir_recursively(WCS_WORK_DIR, pool));

    /* Create a repos. Register it for clean-up. Set *REPOS_URL to its path. */
    {
        svn_repos_t *repos;

        /* Use a subpool to create the repository and then destroy the subpool
           so the repository's underlying filesystem is closed.  If opts->fs_type
           is BDB this prevents any attempt to open a second environment handle
           within the same process when we checkout the WC below.  BDB 4.4+ allows
           only a single environment handle to be open per process. */
        apr_pool_t *subpool = svn_pool_create(pool);

        SVN_ERR(svn_test__create_repos(&repos, repos_path, opts, subpool));
        SVN_ERR(svn_uri_get_file_url_from_dirent(repos_url, repos_path, pool));
        svn_pool_destroy(subpool);
    }

    /* Create a WC. Set *WC_ABSPATH to its path. */
    {
        apr_pool_t *subpool = svn_pool_create(pool); /* To cleanup CTX */
        svn_client_ctx_t *ctx;
        svn_opt_revision_t head_rev = { svn_opt_revision_head, {0} };

        SVN_ERR(svn_client_create_context(&ctx, subpool));
        SVN_ERR(svn_dirent_get_absolute(wc_abspath, wc_path, pool));
        SVN_ERR(svn_client_checkout3(NULL, *repos_url, *wc_abspath,
                                     &head_rev, &head_rev, svn_depth_infinity,
                                     FALSE /* ignore_externals */,
                                     FALSE /* allow_unver_obstructions */,
                                     ctx, subpool));
        svn_pool_destroy(subpool);
    }

    /* Register this WC for cleanup. */
    svn_test_add_dir_cleanup(*wc_abspath);

    return SVN_NO_ERROR;
}
Exemplo n.º 2
0
static svn_error_t *
open_directory(const char *path,
               void *parent_baton,
               svn_revnum_t base_revision,
               apr_pool_t *pool,
               void **child_baton)
{
  struct dir_baton *pb = parent_baton;
  struct edit_baton *eb = pb->edit_baton;
  struct dir_baton *new_db;
  const char *cmp_path = NULL;
  svn_revnum_t cmp_rev = SVN_INVALID_REVNUM;

  /* If the parent directory has explicit comparison path and rev,
     record the same for this one. */
  if (ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev))
    {
      cmp_path = svn_relpath_join(pb->cmp_path,
                                  svn_relpath_basename(path, pool), pool);
      cmp_rev = pb->cmp_rev;
    }

  new_db = make_dir_baton(path, cmp_path, cmp_rev, eb, pb, FALSE, pool);
  *child_baton = new_db;
  return SVN_NO_ERROR;
}
Exemplo n.º 3
0
/* ### FIXME:  Consider somehow sharing code with
   ### libsvn_repos/load-fs-vtable.c:prefix_mergeinfo_paths() */
static svn_error_t *
prefix_mergeinfo_paths(svn_string_t **mergeinfo_val,
                       const svn_string_t *mergeinfo_orig,
                       const char *parent_dir,
                       apr_pool_t *pool)
{
  apr_hash_t *prefixed_mergeinfo, *mergeinfo;
  apr_hash_index_t *hi;
  void *rangelist;

  SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig->data, pool));
  prefixed_mergeinfo = apr_hash_make(pool);
  for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
    {
      const void *key;
      const char *path, *merge_source;

      apr_hash_this(hi, &key, NULL, &rangelist);
      merge_source = svn_relpath_canonicalize(key, pool);

      /* The svn:mergeinfo property syntax demands a repos abspath */
      path = svn_fspath__canonicalize(svn_relpath_join(parent_dir,
                                                       merge_source, pool),
                                      pool);
      svn_hash_sets(prefixed_mergeinfo, path, rangelist);
    }
  return svn_mergeinfo_to_string(mergeinfo_val, prefixed_mergeinfo, pool);
}
Exemplo n.º 4
0
static svn_error_t *
open_file(const char *path,
          void *parent_baton,
          svn_revnum_t ancestor_revision,
          apr_pool_t *pool,
          void **file_baton)
{
  struct dir_baton *pb = parent_baton;
  struct edit_baton *eb = pb->edit_baton;
  const char *cmp_path = NULL;
  svn_revnum_t cmp_rev = SVN_INVALID_REVNUM;

  /* If the parent directory has explicit comparison path and rev,
     record the same for this one. */
  if (ARE_VALID_COPY_ARGS(pb->cmp_path, pb->cmp_rev))
    {
      cmp_path = svn_relpath_join(pb->cmp_path,
                                  svn_relpath_basename(path, pool), pool);
      cmp_rev = pb->cmp_rev;
    }

  SVN_ERR(dump_node(eb, path,
                    svn_node_file, svn_node_action_change,
                    FALSE, cmp_path, cmp_rev, pool));

  *file_baton = NULL;  /* muhahahaha again */
  return SVN_NO_ERROR;
}
Exemplo n.º 5
0
static svn_error_t *
verify_directory_entry(void *baton, const void *key, apr_ssize_t klen,
                       void *val, apr_pool_t *pool)
{
  struct dir_baton *db = baton;
  char *path = svn_relpath_join(db->path, (const char *)key, pool);
  svn_node_kind_t kind;
  apr_hash_t *dirents;
  svn_filesize_t len;

  SVN_ERR(svn_fs_check_path(&kind, db->edit_baton->fs_root, path, pool));
  switch (kind) {
  case svn_node_dir:
    /* Getting this directory's contents is enough to ensure that our
       link to it is correct. */
    SVN_ERR(svn_fs_dir_entries(&dirents, db->edit_baton->fs_root, path, pool));
    break;
  case svn_node_file:
    /* Getting this file's size is enough to ensure that our link to it
       is correct. */
    SVN_ERR(svn_fs_file_length(&len, db->edit_baton->fs_root, path, pool));
    break;
  default:
    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
                             _("Unexpected node kind %d for '%s'"), kind, path);
  }

  return SVN_NO_ERROR;
}
Exemplo n.º 6
0
/* Push information about another directory onto the linked list RB->db.
 *
 * CHILD_BATON is the baton returned by the commit editor. RELPATH is the
 * repository-relative path of this directory. IS_ADDED is true iff this
 * directory is being added (with or without history). If added with
 * history then COPYFROM_PATH/COPYFROM_REV are the copyfrom source, else
 * are NULL/SVN_INVALID_REVNUM.
 */
static void
push_directory(struct revision_baton *rb,
               void *child_baton,
               const char *relpath,
               svn_boolean_t is_added,
               const char *copyfrom_path,
               svn_revnum_t copyfrom_rev)
{
  struct directory_baton *child_db = apr_pcalloc(rb->pool, sizeof (*child_db));

  SVN_ERR_ASSERT_NO_RETURN(
    is_added || (copyfrom_path == NULL && copyfrom_rev == SVN_INVALID_REVNUM));

  /* If this node is an existing (not newly added) child of a copied node,
     calculate where it was copied from. */
  if (!is_added
      && ARE_VALID_COPY_ARGS(rb->db->copyfrom_path, rb->db->copyfrom_rev))
    {
      const char *name = svn_relpath_basename(relpath, NULL);

      copyfrom_path = svn_relpath_join(rb->db->copyfrom_path, name,
                                       rb->pool);
      copyfrom_rev = rb->db->copyfrom_rev;
    }

  child_db->baton = child_baton;
  child_db->relpath = relpath;
  child_db->copyfrom_path = copyfrom_path;
  child_db->copyfrom_rev = copyfrom_rev;
  child_db->parent = rb->db;
  rb->db = child_db;
}
Exemplo n.º 7
0
/* Helper func for recursively fetching svn_dirent_t's from a remote
   directory and pushing them at an info-receiver callback.

   DEPTH is the depth starting at DIR, even though RECEIVER is never
   invoked on DIR: if DEPTH is svn_depth_immediates, then invoke
   RECEIVER on all children of DIR, but none of their children; if
   svn_depth_files, then invoke RECEIVER on file children of DIR but
   not on subdirectories; if svn_depth_infinity, recurse fully.
   DIR is a relpath, relative to the root of RA_SESSION.
*/
static svn_error_t *
push_dir_info(svn_ra_session_t *ra_session,
              const svn_client__pathrev_t *pathrev,
              const char *dir,
              svn_client_info_receiver2_t receiver,
              void *receiver_baton,
              svn_depth_t depth,
              svn_client_ctx_t *ctx,
              apr_hash_t *locks,
              apr_pool_t *pool)
{
  apr_hash_t *tmpdirents;
  apr_hash_index_t *hi;
  apr_pool_t *subpool = svn_pool_create(pool);

  SVN_ERR(svn_ra_get_dir2(ra_session, &tmpdirents, NULL, NULL,
                          dir, pathrev->rev, DIRENT_FIELDS, pool));

  for (hi = apr_hash_first(pool, tmpdirents); hi; hi = apr_hash_next(hi))
    {
      const char *path, *fs_path;
      svn_lock_t *lock;
      svn_client_info2_t *info;
      const char *name = svn__apr_hash_index_key(hi);
      svn_dirent_t *the_ent = svn__apr_hash_index_val(hi);
      svn_client__pathrev_t *child_pathrev;

      svn_pool_clear(subpool);

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

      path = svn_relpath_join(dir, name, subpool);
      child_pathrev = svn_client__pathrev_join_relpath(pathrev, name, subpool);
      fs_path = svn_client__pathrev_fspath(child_pathrev, subpool);

      lock = apr_hash_get(locks, fs_path, APR_HASH_KEY_STRING);

      SVN_ERR(build_info_from_dirent(&info, the_ent, lock, child_pathrev,
                                     subpool));

      if (depth >= svn_depth_immediates
          || (depth == svn_depth_files && the_ent->kind == svn_node_file))
        {
          SVN_ERR(receiver(receiver_baton, path, info, subpool));
        }

      if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir)
        {
          SVN_ERR(push_dir_info(ra_session, child_pathrev, path,
                                receiver, receiver_baton,
                                depth, ctx, locks, subpool));
        }
    }

  svn_pool_destroy(subpool);

  return SVN_NO_ERROR;
}
Exemplo n.º 8
0
svn_error_t *
svn_editor_alter_directory(svn_editor_t *editor,
                           const char *relpath,
                           svn_revnum_t revision,
                           const apr_array_header_t *children,
                           apr_hash_t *props)
{
  svn_error_t *err = SVN_NO_ERROR;

  SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
  SVN_ERR_ASSERT(children != NULL || props != NULL);
  /* ### validate children are just basenames?  */
  SHOULD_NOT_BE_FINISHED(editor);
  SHOULD_ALLOW_ALTER(editor, relpath);
  VERIFY_PARENT_MAY_EXIST(editor, relpath);

  SVN_ERR(check_cancel(editor));

  if (editor->funcs.cb_alter_directory)
    {
      START_CALLBACK(editor);
      err = editor->funcs.cb_alter_directory(editor->baton,
                                             relpath, revision,
                                             children, props,
                                             editor->scratch_pool);
      END_CALLBACK(editor);
    }

  MARK_COMPLETED(editor, relpath);
  MARK_PARENT_STABLE(editor, relpath);

#ifdef ENABLE_ORDERING_CHECK
  /* ### this is not entirely correct. we probably need to adjust the
     ### check_unknown_child() function for this scenario.  */
#if 0
  {
    int i;
    for (i = 0; i < children->nelts; i++)
      {
        const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
        const char *child = svn_relpath_join(relpath, child_basename,
                                             editor->state_pool);

        apr_hash_set(editor->pending_incomplete_children, child,
                     APR_HASH_KEY_STRING, "");
        /* Perhaps MARK_ALLOW_ADD(editor, child); ? */
      }
  }
#endif
#endif

  svn_pool_clear(editor->scratch_pool);
  return svn_error_trace(err);
}
Exemplo n.º 9
0
svn_error_t *
svn_editor_add_directory(svn_editor_t *editor,
                         const char *relpath,
                         const apr_array_header_t *children,
                         apr_hash_t *props,
                         svn_revnum_t replaces_rev)
{
  svn_error_t *err = SVN_NO_ERROR;

  SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
  SVN_ERR_ASSERT(children != NULL);
  SVN_ERR_ASSERT(props != NULL);
  /* ### validate children are just basenames?  */
  SHOULD_NOT_BE_FINISHED(editor);
  SHOULD_ALLOW_ADD(editor, relpath);
  VERIFY_PARENT_MAY_EXIST(editor, relpath);
  CHECK_UNKNOWN_CHILD(editor, relpath);

  SVN_ERR(check_cancel(editor));

  if (editor->funcs.cb_add_directory)
    {
      START_CALLBACK(editor);
      err = editor->funcs.cb_add_directory(editor->baton, relpath, children,
                                           props, replaces_rev,
                                           editor->scratch_pool);
      END_CALLBACK(editor);
    }

  MARK_ADDED_DIR(editor, relpath);
  MARK_PARENT_STABLE(editor, relpath);
  CLEAR_INCOMPLETE(editor, relpath);

#ifdef ENABLE_ORDERING_CHECK
  {
    int i;
    for (i = 0; i < children->nelts; i++)
      {
        const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
        const char *child = svn_relpath_join(relpath, child_basename,
                                             editor->state_pool);

        svn_hash_sets(editor->pending_incomplete_children, child, "");
      }
  }
#endif

  svn_pool_clear(editor->scratch_pool);
  return svn_error_trace(err);
}
Exemplo n.º 10
0
/* Set *FS_PATH_P to the absolute filesystem path associated with the
   URL built from SESSION's URL and REL_PATH (which is relative to
   session's URL.  Use POOL for allocations. */
static svn_error_t *
get_fs_path(const char **fs_path_p,
            svn_ra_session_t *session,
            const char *rel_path,
            apr_pool_t *pool)
{
  const char *url, *fs_path;

  SVN_ERR(svn_ra_get_session_url(session, &url, pool));
  SVN_ERR(svn_ra_get_path_relative_to_root(session, &fs_path, url, pool));
  *fs_path_p = svn_fspath__canonicalize(svn_relpath_join(fs_path,
                                                         rel_path, pool),
                                        pool);
  return SVN_NO_ERROR;
}
Exemplo n.º 11
0
/* Make a directory baton to represent the directory was path
   (relative to EDIT_BATON's path) is PATH.

   CMP_PATH/CMP_REV are the path/revision against which this directory
   should be compared for changes.  If either is omitted (NULL for the
   path, SVN_INVALID_REVNUM for the rev), just compare this directory
   PATH against itself in the previous revision.

   PARENT_DIR_BATON is the directory baton of this directory's parent,
   or NULL if this is the top-level directory of the edit.  ADDED
   indicated if this directory is newly added in this revision.
   Perform all allocations in POOL.  */
static struct dir_baton *
make_dir_baton(const char *path,
               const char *cmp_path,
               svn_revnum_t cmp_rev,
               void *edit_baton,
               void *parent_dir_baton,
               svn_boolean_t added,
               apr_pool_t *pool)
{
  struct edit_baton *eb = edit_baton;
  struct dir_baton *pb = parent_dir_baton;
  struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
  const char *full_path;

  /* A path relative to nothing?  I don't think so. */
  SVN_ERR_ASSERT_NO_RETURN(!path || pb);

  /* Construct the full path of this node. */
  if (pb)
    full_path = svn_relpath_join(eb->path, path, pool);
  else
    full_path = apr_pstrdup(pool, eb->path);

  /* Remove leading slashes from copyfrom paths. */
  if (cmp_path)
    cmp_path = svn_relpath_canonicalize(cmp_path, pool);

  new_db->edit_baton = eb;
  new_db->parent_dir_baton = pb;
  new_db->path = full_path;
  new_db->cmp_path = cmp_path;
  new_db->cmp_rev = cmp_rev;
  new_db->added = added;
  new_db->written_out = FALSE;
  new_db->deleted_entries = apr_hash_make(pool);
  new_db->pool = pool;

  return new_db;
}
Exemplo n.º 12
0
/* Add PATH to the operations tree rooted at OPERATION, creating any
   intermediate nodes that are required.  Here's what's expected for
   each action type:

      ACTION          URL    REV      SRC-FILE  PROPNAME
      ------------    -----  -------  --------  --------
      ACTION_MKDIR    NULL   invalid  NULL      NULL
      ACTION_CP       valid  valid    NULL      NULL
      ACTION_PUT      NULL   invalid  valid     NULL
      ACTION_RM       NULL   invalid  NULL      NULL
      ACTION_PROPSET  valid  invalid  NULL      valid

   Node type information is obtained for any copy source (to determine
   whether to create a file or directory) and for any deleted path (to
   ensure it exists since svn_delta_editor_t->delete_entry doesn't
   return an error on non-existent nodes). */
static svn_error_t *
build(struct editor_baton *eb,
      action_code_t action,
      const char *relpath,
      const char *url,
      svn_revnum_t rev,
      apr_hash_t *props,
      const char *src_file,
      svn_revnum_t head,
      apr_pool_t *scratch_pool)
{
  apr_array_header_t *path_bits = svn_path_decompose(relpath, scratch_pool);
  const char *path_so_far = "";
  struct operation *operation = &eb->root;
  int i;

  /* We should only see PROPS when action is ACTION_PROPSET. */
  SVN_ERR_ASSERT((props && action == ACTION_PROPSET)
                || (!props && action != ACTION_PROPSET) );

  /* Look for any previous operations we've recognized for PATH.  If
     any of PATH's ancestors have not yet been traversed, we'll be
     creating OP_OPEN operations for them as we walk down PATH's path
     components. */
  for (i = 0; i < path_bits->nelts; ++i)
    {
      const char *path_bit = APR_ARRAY_IDX(path_bits, i, const char *);
      path_so_far = svn_relpath_join(path_so_far, path_bit, scratch_pool);
      operation = get_operation(path_so_far, operation, eb->edit_pool);
    }

  /* Handle property changes. */
  if (props)
    {
      SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
                                  relpath, scratch_pool));

      /* If we're not adding this thing ourselves, check for existence.  */
      if (! ((operation->operation == OP_ADD) ||
             (operation->operation == OP_REPLACE)))
        {
          if ((operation->kind == svn_node_file)
                   && (operation->operation == OP_OPEN))
            operation->operation = OP_PROPSET;
        }
      operation->props = svn_prop_hash_dup(props, eb->edit_pool);
      if (!operation->rev)
        operation->rev = rev;
      return SVN_NO_ERROR;
    }

  if (action == ACTION_RM)
    operation->operation = OP_DELETE;

  /* Handle copy operations (which can be adds or replacements). */
  else if (action == ACTION_CP)
    {
      operation->operation =
        operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;

      SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
                                  relpath, scratch_pool));
      operation->url = url;
      operation->rev = rev;
    }
  /* Handle mkdir operations (which can be adds or replacements). */
  else if (action == ACTION_MKDIR)
    {
      operation->operation =
        operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
      operation->kind = svn_node_dir;
    }
  /* Handle put operations (which can be adds, replacements, or opens). */
  else if (action == ACTION_PUT)
    {
      if (operation->operation == OP_DELETE)
        {
          operation->operation = OP_REPLACE;
        }
      else
        {
          SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
                                      relpath, scratch_pool));
          if (operation->kind == svn_node_file)
            operation->operation = OP_OPEN;
          else if (operation->kind == svn_node_none)
            operation->operation = OP_ADD;
          else
            return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                     "'%s' is not a file", relpath);
        }
      operation->kind = svn_node_file;
      operation->src_file = src_file;
    }
  else
    {
      /* We shouldn't get here. */
      SVN_ERR_MALFUNCTION();
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 13
0
/* Helper for the remote case of svn_client_proplist.
 *
 * Push a new 'svn_client_proplist_item_t *' item onto PROPLIST,
 * containing the properties for "TARGET_PREFIX/TARGET_RELATIVE" in
 * REVNUM, obtained using RA_LIB and SESSION.  The item->node_name
 * will be "TARGET_PREFIX/TARGET_RELATIVE", and the value will be a
 * hash mapping 'const char *' property names onto 'svn_string_t *'
 * property values.
 *
 * Allocate the new item and its contents in POOL.
 * Do all looping, recursion, and temporary work in SCRATCHPOOL.
 *
 * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE".
 *
 * If the target is a directory, only fetch properties for the files
 * and directories at depth DEPTH.
 */
static svn_error_t *
remote_proplist(const char *target_prefix,
                const char *target_relative,
                svn_node_kind_t kind,
                svn_revnum_t revnum,
                svn_ra_session_t *ra_session,
                svn_depth_t depth,
                svn_proplist_receiver_t receiver,
                void *receiver_baton,
                apr_pool_t *pool,
                apr_pool_t *scratchpool)
{
  apr_hash_t *dirents;
  apr_hash_t *prop_hash, *final_hash;
  apr_hash_index_t *hi;
  const char *target_full_url =
    svn_path_url_add_component2(target_prefix, target_relative, scratchpool);

  if (kind == svn_node_dir)
    {
      SVN_ERR(svn_ra_get_dir2(ra_session,
                              (depth > svn_depth_empty) ? &dirents : NULL,
                              NULL, &prop_hash, target_relative, revnum,
                              SVN_DIRENT_KIND, scratchpool));
    }
  else if (kind == svn_node_file)
    {
      SVN_ERR(svn_ra_get_file(ra_session, target_relative, revnum,
                              NULL, NULL, &prop_hash, scratchpool));
    }
  else
    {
      return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
                               _("Unknown node kind for '%s'"),
                               target_full_url);
    }

  /* Filter out non-regular properties, since the RA layer returns all
     kinds.  Copy regular properties keys/vals from the prop_hash
     allocated in SCRATCHPOOL to the "final" hash allocated in POOL. */
  final_hash = apr_hash_make(pool);
  for (hi = apr_hash_first(scratchpool, prop_hash);
       hi;
       hi = apr_hash_next(hi))
    {
      const char *name = svn__apr_hash_index_key(hi);
      apr_ssize_t klen = svn__apr_hash_index_klen(hi);
      svn_string_t *value = svn__apr_hash_index_val(hi);
      svn_prop_kind_t prop_kind;

      prop_kind = svn_property_kind(NULL, name);

      if (prop_kind == svn_prop_regular_kind)
        {
          name = apr_pstrdup(pool, name);
          value = svn_string_dup(value, pool);
          apr_hash_set(final_hash, name, klen, value);
        }
    }

  SVN_ERR(call_receiver(target_full_url, final_hash, receiver, receiver_baton,
                        pool));

  if (depth > svn_depth_empty
      && (kind == svn_node_dir) && (apr_hash_count(dirents) > 0))
    {
      apr_pool_t *subpool = svn_pool_create(scratchpool);

      for (hi = apr_hash_first(scratchpool, dirents);
           hi;
           hi = apr_hash_next(hi))
        {
          const char *this_name = svn__apr_hash_index_key(hi);
          svn_dirent_t *this_ent = svn__apr_hash_index_val(hi);
          const char *new_target_relative;

          svn_pool_clear(subpool);

          new_target_relative = svn_relpath_join(target_relative,
                                                 this_name, subpool);

          if (this_ent->kind == svn_node_file
              || depth > svn_depth_files)
            {
              svn_depth_t depth_below_here = depth;

              if (depth == svn_depth_immediates)
                depth_below_here = svn_depth_empty;

              SVN_ERR(remote_proplist(target_prefix,
                                      new_target_relative,
                                      this_ent->kind,
                                      revnum,
                                      ra_session,
                                      depth_below_here,
                                      receiver,
                                      receiver_baton,
                                      pool,
                                      subpool));
            }
        }

      svn_pool_destroy(subpool);
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 14
0
/* Helper for the remote case of svn_client_propget.
 *
 * Get the value of property PROPNAME in REVNUM, using RA_LIB and
 * SESSION.  Store the value ('svn_string_t *') in PROPS, under the
 * path key "TARGET_PREFIX/TARGET_RELATIVE" ('const char *').
 *
 * Recurse according to DEPTH, similarly to svn_client_propget3().
 *
 * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE".
 * Yes, caller passes this; it makes the recursion more efficient :-).
 *
 * Allocate the keys and values in PERM_POOL, but do all temporary
 * work in WORK_POOL.  The two pools can be the same; recursive
 * calls may use a different WORK_POOL, however.
 */
static svn_error_t *
remote_propget(apr_hash_t *props,
               const char *propname,
               const char *target_prefix,
               const char *target_relative,
               svn_node_kind_t kind,
               svn_revnum_t revnum,
               svn_ra_session_t *ra_session,
               svn_depth_t depth,
               apr_pool_t *perm_pool,
               apr_pool_t *work_pool)
{
  apr_hash_t *dirents;
  apr_hash_t *prop_hash;
  const svn_string_t *val;
  const char *target_full_url =
    svn_path_url_add_component2(target_prefix, target_relative, work_pool);

  if (kind == svn_node_dir)
    {
      SVN_ERR(svn_ra_get_dir2(ra_session,
                              (depth >= svn_depth_files ? &dirents : NULL),
                              NULL, &prop_hash, target_relative, revnum,
                              SVN_DIRENT_KIND, work_pool));
    }
  else if (kind == svn_node_file)
    {
      SVN_ERR(svn_ra_get_file(ra_session, target_relative, revnum,
                              NULL, NULL, &prop_hash, work_pool));
    }
  else if (kind == svn_node_none)
    {
      return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
                               _("'%s' does not exist in revision %ld"),
                               target_full_url, revnum);
    }
  else
    {
      return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
                               _("Unknown node kind for '%s'"),
                               target_full_url);
    }

  if ((val = apr_hash_get(prop_hash, propname, APR_HASH_KEY_STRING)))
    {
      apr_hash_set(props, apr_pstrdup(perm_pool, target_full_url),
                   APR_HASH_KEY_STRING, svn_string_dup(val, perm_pool));
    }

  if (depth >= svn_depth_files
      && kind == svn_node_dir
      && apr_hash_count(dirents) > 0)
    {
      apr_hash_index_t *hi;
      apr_pool_t *iterpool = svn_pool_create(work_pool);

      for (hi = apr_hash_first(work_pool, dirents);
           hi;
           hi = apr_hash_next(hi))
        {
          const char *this_name = svn__apr_hash_index_key(hi);
          svn_dirent_t *this_ent = svn__apr_hash_index_val(hi);
          const char *new_target_relative;
          svn_depth_t depth_below_here = depth;

          svn_pool_clear(iterpool);

          if (depth == svn_depth_files && this_ent->kind == svn_node_dir)
            continue;

          if (depth == svn_depth_files || depth == svn_depth_immediates)
            depth_below_here = svn_depth_empty;

          new_target_relative = svn_relpath_join(target_relative, this_name,
                                                 iterpool);

          SVN_ERR(remote_propget(props,
                                 propname,
                                 target_prefix,
                                 new_target_relative,
                                 this_ent->kind,
                                 revnum,
                                 ra_session,
                                 depth_below_here,
                                 perm_pool, iterpool));
        }

      svn_pool_destroy(iterpool);
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 15
0
/* Make a node baton, parsing the relevant HEADERS.
 *
 * If RB->pb->parent_dir:
 *   prefix it to NB->path
 *   prefix it to NB->copyfrom_path (if present)
 */
static svn_error_t *
make_node_baton(struct node_baton **node_baton_p,
                apr_hash_t *headers,
                struct revision_baton *rb,
                apr_pool_t *pool)
{
  struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb));
  const char *val;

  /* Start with sensible defaults. */
  nb->rb = rb;
  nb->pool = pool;
  nb->kind = svn_node_unknown;

  /* Then add info from the headers.  */
  if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH)))
  {
    val = svn_relpath_canonicalize(val, pool);
    if (rb->pb->parent_dir)
      nb->path = svn_relpath_join(rb->pb->parent_dir, val, pool);
    else
      nb->path = val;
  }

  if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND)))
    {
      if (! strcmp(val, "file"))
        nb->kind = svn_node_file;
      else if (! strcmp(val, "dir"))
        nb->kind = svn_node_dir;
    }

  nb->action = (enum svn_node_action)(-1);  /* an invalid action code */
  if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_ACTION)))
    {
      if (! strcmp(val, "change"))
        nb->action = svn_node_action_change;
      else if (! strcmp(val, "add"))
        nb->action = svn_node_action_add;
      else if (! strcmp(val, "delete"))
        nb->action = svn_node_action_delete;
      else if (! strcmp(val, "replace"))
        nb->action = svn_node_action_replace;
    }

  nb->copyfrom_rev = SVN_INVALID_REVNUM;
  if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV)))
    {
      nb->copyfrom_rev = SVN_STR_TO_REV(val);
    }
  if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH)))
    {
      val = svn_relpath_canonicalize(val, pool);
      if (rb->pb->parent_dir)
        nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir, val, pool);
      else
        nb->copyfrom_path = val;
    }

  if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_CHECKSUM)))
    {
      SVN_ERR(svn_checksum_parse_hex(&nb->result_checksum, svn_checksum_md5,
                                     val, pool));
    }

  if ((val = svn_hash_gets(headers,
                           SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_CHECKSUM)))
    {
      SVN_ERR(svn_checksum_parse_hex(&nb->base_checksum, svn_checksum_md5, val,
                                     pool));
    }

  if ((val = svn_hash_gets(headers,
                           SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_CHECKSUM)))
    {
      SVN_ERR(svn_checksum_parse_hex(&nb->copy_source_checksum,
                                     svn_checksum_md5, val, pool));
    }

  /* What's cool about this dump format is that the parser just
     ignores any unrecognized headers.  :-)  */

  *node_baton_p = nb;
  return SVN_NO_ERROR;
}
Exemplo n.º 16
0
/* ### TODO: Handle depth. */
static svn_error_t *
diff_deleted_dir(const char *dir,
                 svn_revnum_t revision,
                 svn_ra_session_t *ra_session,
                 void *edit_baton,
                 svn_cancel_func_t cancel_func,
                 void *cancel_baton,
                 apr_pool_t *pool)
{
  struct edit_baton *eb = edit_baton;
  apr_hash_t *dirents;
  apr_pool_t *iterpool = svn_pool_create(pool);
  apr_hash_index_t *hi;

  if (cancel_func)
    SVN_ERR(cancel_func(cancel_baton));

  SVN_ERR(svn_ra_get_dir2(ra_session,
                          &dirents,
                          NULL, NULL,
                          dir,
                          revision,
                          SVN_DIRENT_KIND,
                          pool));

  for (hi = apr_hash_first(pool, dirents); hi;
       hi = apr_hash_next(hi))
    {
      const char *path;
      const char *name = svn__apr_hash_index_key(hi);
      svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
      svn_node_kind_t kind;
      svn_client_diff_summarize_t *sum;

      svn_pool_clear(iterpool);

      path = svn_relpath_join(dir, name, iterpool);

      SVN_ERR(svn_ra_check_path(eb->ra_session,
                                path,
                                eb->revision,
                                &kind,
                                iterpool));

      sum = apr_pcalloc(iterpool, sizeof(*sum));
      sum->summarize_kind = svn_client_diff_summarize_kind_deleted;
      sum->path = path;
      sum->node_kind = kind;

      SVN_ERR(eb->summarize_func(sum,
                                 eb->summarize_func_baton,
                                 iterpool));

      if (dirent->kind == svn_node_dir)
        SVN_ERR(diff_deleted_dir(path,
                                 revision,
                                 ra_session,
                                 eb,
                                 cancel_func,
                                 cancel_baton,
                                 iterpool));
    }

  svn_pool_destroy(iterpool);
  return SVN_NO_ERROR;
}
Exemplo n.º 17
0
/* Recursively traverse EDIT_PATH (as it exists under SOURCE_ROOT) emitting
   the appropriate editor calls to add it and its children without any
   history.  This is meant to be used when either a subset of the tree
   has been ignored and we need to copy something from that subset to
   the part of the tree we do care about, or if a subset of the tree is
   unavailable because of authz and we need to use it as the source of
   a copy. */
static svn_error_t *
add_subdir(svn_fs_root_t *source_root,
           svn_fs_root_t *target_root,
           const svn_delta_editor_t *editor,
           void *edit_baton,
           const char *edit_path,
           void *parent_baton,
           const char *source_fspath,
           svn_repos_authz_func_t authz_read_func,
           void *authz_read_baton,
           apr_hash_t *changed_paths,
           apr_pool_t *pool,
           void **dir_baton)
{
  apr_pool_t *subpool = svn_pool_create(pool);
  apr_hash_index_t *hi, *phi;
  apr_hash_t *dirents;
  apr_hash_t *props;

  SVN_ERR(editor->add_directory(edit_path, parent_baton, NULL,
                                SVN_INVALID_REVNUM, pool, dir_baton));

  SVN_ERR(svn_fs_node_proplist(&props, target_root, edit_path, pool));

  for (phi = apr_hash_first(pool, props); phi; phi = apr_hash_next(phi))
    {
      const void *key;
      void *val;

      svn_pool_clear(subpool);
      apr_hash_this(phi, &key, NULL, &val);
      SVN_ERR(editor->change_dir_prop(*dir_baton, key, val, subpool));
    }

  /* We have to get the dirents from the source path, not the target,
     because we want nested copies from *readable* paths to be handled by
     path_driver_cb_func, not add_subdir (in order to preserve history). */
  SVN_ERR(svn_fs_dir_entries(&dirents, source_root, source_fspath, pool));

  for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))
    {
      svn_fs_path_change2_t *change;
      svn_boolean_t readable = TRUE;
      svn_fs_dirent_t *dent;
      const char *copyfrom_path = NULL;
      svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
      const char *new_edit_path;
      void *val;

      svn_pool_clear(subpool);

      apr_hash_this(hi, NULL, NULL, &val);

      dent = val;

      new_edit_path = svn_relpath_join(edit_path, dent->name, subpool);

      /* If a file or subdirectory of the copied directory is listed as a
         changed path (because it was modified after the copy but before the
         commit), we remove it from the changed_paths hash so that future
         calls to path_driver_cb_func will ignore it. */
      change = apr_hash_get(changed_paths, new_edit_path, APR_HASH_KEY_STRING);
      if (change)
        {
          apr_hash_set(changed_paths, new_edit_path, APR_HASH_KEY_STRING, NULL);

          /* If it's a delete, skip this entry. */
          if (change->change_kind == svn_fs_path_change_delete)
            continue;

          /* If it's a replacement, check for copyfrom info (if we
             don't have it already. */
          if (change->change_kind == svn_fs_path_change_replace)
            {
              if (! change->copyfrom_known)
                {
                  SVN_ERR(svn_fs_copied_from(&change->copyfrom_rev,
                                             &change->copyfrom_path,
                                             target_root, new_edit_path, pool));
                  change->copyfrom_known = TRUE;
                }
              copyfrom_path = change->copyfrom_path;
              copyfrom_rev = change->copyfrom_rev;
            }
        }

      if (authz_read_func)
        SVN_ERR(authz_read_func(&readable, target_root, new_edit_path,
                                authz_read_baton, pool));

      if (! readable)
        continue;

      if (dent->kind == svn_node_dir)
        {
          svn_fs_root_t *new_source_root;
          const char *new_source_fspath;
          void *new_dir_baton;

          if (copyfrom_path)
            {
              svn_fs_t *fs = svn_fs_root_fs(source_root);
              SVN_ERR(svn_fs_revision_root(&new_source_root, fs,
                                           copyfrom_rev, pool));
              new_source_fspath = copyfrom_path;
            }
          else
            {
              new_source_root = source_root;
              new_source_fspath = svn_fspath__join(source_fspath, dent->name,
                                                   subpool);
            }

          /* ### authz considerations?
           *
           * I think not; when path_driver_cb_func() calls add_subdir(), it
           * passes SOURCE_ROOT and SOURCE_FSPATH that are unreadable.
           */
          if (change && change->change_kind == svn_fs_path_change_replace
              && copyfrom_path == NULL)
            {
              SVN_ERR(editor->add_directory(new_edit_path, *dir_baton,
                                            NULL, SVN_INVALID_REVNUM,
                                            subpool, &new_dir_baton));
            }
          else
            {
              SVN_ERR(add_subdir(new_source_root, target_root,
                                 editor, edit_baton, new_edit_path,
                                 *dir_baton, new_source_fspath,
                                 authz_read_func, authz_read_baton,
                                 changed_paths, subpool, &new_dir_baton));
            }

          SVN_ERR(editor->close_directory(new_dir_baton, subpool));
        }
      else if (dent->kind == svn_node_file)
        {
          svn_txdelta_window_handler_t delta_handler;
          void *delta_handler_baton, *file_baton;
          svn_txdelta_stream_t *delta_stream;
          svn_checksum_t *checksum;

          SVN_ERR(editor->add_file(new_edit_path, *dir_baton, NULL,
                                   SVN_INVALID_REVNUM, pool, &file_baton));

          SVN_ERR(svn_fs_node_proplist(&props, target_root,
                                       new_edit_path, subpool));

          for (phi = apr_hash_first(pool, props); phi; phi = apr_hash_next(phi))
            {
              const void *key;

              apr_hash_this(phi, &key, NULL, &val);
              SVN_ERR(editor->change_file_prop(file_baton, key, val, subpool));
            }

          SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool,
                                          &delta_handler,
                                          &delta_handler_baton));

          SVN_ERR(svn_fs_get_file_delta_stream(&delta_stream, NULL, NULL,
                                               target_root, new_edit_path,
                                               pool));

          SVN_ERR(svn_txdelta_send_txstream(delta_stream,
                                            delta_handler,
                                            delta_handler_baton,
                                            pool));

          SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, target_root,
                                       new_edit_path, TRUE, pool));
          SVN_ERR(editor->close_file(file_baton,
                                     svn_checksum_to_cstring(checksum, pool),
                                     pool));
        }
      else
        SVN_ERR_MALFUNCTION();
    }

  svn_pool_destroy(subpool);

  return SVN_NO_ERROR;
}
Exemplo n.º 18
0
/* Produce a diff of depth DEPTH for the directory at LOCAL_ABSPATH,
 * using information from the arbitrary_diff_walker_baton B.
 * LOCAL_ABSPATH is the path being crawled and can be on either side
 * of the diff depending on baton->recursing_within_added_subtree. */
static svn_error_t *
arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b,
                        const char *local_abspath,
                        svn_depth_t depth,
                        apr_pool_t *scratch_pool)
{
  const char *local_abspath1;
  const char *local_abspath2;
  svn_node_kind_t kind1;
  svn_node_kind_t kind2;
  const char *child_relpath;
  apr_hash_t *dirents1;
  apr_hash_t *dirents2;
  apr_hash_t *merged_dirents;
  apr_array_header_t *sorted_dirents;
  int i;
  apr_pool_t *iterpool;

  if (b->recursing_within_adm_dir)
    {
      if (svn_dirent_skip_ancestor(b->adm_dir_abspath, local_abspath))
        return SVN_NO_ERROR;
      else
        {
          b->recursing_within_adm_dir = FALSE;
          b->adm_dir_abspath = NULL;
        }
    }
  else if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL),
                             scratch_pool))
    {
      b->recursing_within_adm_dir = TRUE;
      b->adm_dir_abspath = apr_pstrdup(b->pool, local_abspath);
      return SVN_NO_ERROR;
    }

  if (b->recursing_within_added_subtree)
    child_relpath = svn_dirent_skip_ancestor(b->root2_abspath, local_abspath);
  else
    child_relpath = svn_dirent_skip_ancestor(b->root1_abspath, local_abspath);
  if (!child_relpath)
    return SVN_NO_ERROR;

  local_abspath1 = svn_dirent_join(b->root1_abspath, child_relpath,
                                   scratch_pool);
  SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));

  local_abspath2 = svn_dirent_join(b->root2_abspath, child_relpath,
                                   scratch_pool);
  SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool));

  if (depth > svn_depth_empty)
    {
      if (kind1 == svn_node_dir)
        SVN_ERR(svn_io_get_dirents3(&dirents1, local_abspath1,
                                    TRUE, /* only_check_type */
                                    scratch_pool, scratch_pool));
      else
        dirents1 = apr_hash_make(scratch_pool);
    }

  if (kind2 == svn_node_dir)
    {
      apr_hash_t *original_props;
      apr_hash_t *modified_props;
      apr_array_header_t *prop_changes;

      /* Show any property changes for this directory. */
      SVN_ERR(get_props(&original_props, local_abspath1, b->ctx->wc_ctx,
                        scratch_pool, scratch_pool));
      SVN_ERR(get_props(&modified_props, local_abspath2, b->ctx->wc_ctx,
                        scratch_pool, scratch_pool));
      SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props,
                             scratch_pool));
      if (prop_changes->nelts > 0)
        SVN_ERR(b->callbacks->dir_props_changed(NULL, NULL, child_relpath,
                                                FALSE /* was_added */,
                                                prop_changes, original_props,
                                                b->diff_baton,
                                                scratch_pool));

      if (depth > svn_depth_empty)
        {
          /* Read directory entries. */
          SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2,
                                      TRUE, /* only_check_type */
                                      scratch_pool, scratch_pool));
        }
    }
  else if (depth > svn_depth_empty)
    dirents2 = apr_hash_make(scratch_pool);

  if (depth <= svn_depth_empty)
    return SVN_NO_ERROR;

  /* Compare dirents1 to dirents2 and show added/deleted/changed files. */
  merged_dirents = apr_hash_merge(scratch_pool, dirents1, dirents2,
                                  NULL, NULL);
  sorted_dirents = svn_sort__hash(merged_dirents,
                                  svn_sort_compare_items_as_paths,
                                  scratch_pool);
  iterpool = svn_pool_create(scratch_pool);
  for (i = 0; i < sorted_dirents->nelts; i++)
    {
      svn_sort__item_t elt = APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t);
      const char *name = elt.key;
      svn_io_dirent2_t *dirent1;
      svn_io_dirent2_t *dirent2;
      const char *child1_abspath;
      const char *child2_abspath;

      svn_pool_clear(iterpool);

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

      if (strcmp(name, SVN_WC_ADM_DIR_NAME) == 0)
        continue;

      dirent1 = svn_hash_gets(dirents1, name);
      if (!dirent1)
        {
          dirent1 = svn_io_dirent2_create(iterpool);
          dirent1->kind = svn_node_none;
        }
      dirent2 = svn_hash_gets(dirents2, name);
      if (!dirent2)
        {
          dirent2 = svn_io_dirent2_create(iterpool);
          dirent2->kind = svn_node_none;
        }

      child1_abspath = svn_dirent_join(local_abspath1, name, iterpool);
      child2_abspath = svn_dirent_join(local_abspath2, name, iterpool);

      if (dirent1->special)
        SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent1->kind,
                                           iterpool));
      if (dirent2->special)
        SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent2->kind,
                                           iterpool));

      if (dirent1->kind == svn_node_dir &&
          dirent2->kind == svn_node_dir)
        {
          if (depth == svn_depth_immediates)
            {
              /* Not using the walker, so show property diffs on these dirs. */
              SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath,
                                             b->root1_abspath, b->root2_abspath,
                                             svn_depth_empty,
                                             b->callbacks, b->diff_baton,
                                             b->ctx, iterpool));
            }
          else
            {
              /* Either the walker will visit these directories (with
               * depth=infinity) and they will be processed as 'this dir'
               * later, or we're showing file children only (depth=files). */
              continue;
            }

        }

      /* Files that exist only in dirents1. */
      if (dirent1->kind == svn_node_file &&
          (dirent2->kind == svn_node_dir || dirent2->kind == svn_node_none))
        SVN_ERR(do_arbitrary_files_diff(child1_abspath, b->empty_file_abspath,
                                        svn_relpath_join(child_relpath, name,
                                                         iterpool),
                                        FALSE, TRUE, NULL,
                                        b->callbacks, b->diff_baton,
                                        b->ctx, iterpool));

      /* Files that exist only in dirents2. */
      if (dirent2->kind == svn_node_file &&
          (dirent1->kind == svn_node_dir || dirent1->kind == svn_node_none))
        {
          apr_hash_t *original_props;

          SVN_ERR(get_props(&original_props, child1_abspath, b->ctx->wc_ctx,
                            scratch_pool, scratch_pool));
          SVN_ERR(do_arbitrary_files_diff(b->empty_file_abspath, child2_abspath,
                                          svn_relpath_join(child_relpath, name,
                                                           iterpool),
                                          TRUE, FALSE, original_props,
                                          b->callbacks, b->diff_baton,
                                          b->ctx, iterpool));
        }

      /* Files that exist in dirents1 and dirents2. */
      if (dirent1->kind == svn_node_file && dirent2->kind == svn_node_file)
        SVN_ERR(do_arbitrary_files_diff(child1_abspath, child2_abspath,
                                        svn_relpath_join(child_relpath, name,
                                                         iterpool),
                                        FALSE, FALSE, NULL,
                                        b->callbacks, b->diff_baton,
                                        b->ctx, scratch_pool));

      /* Directories that only exist in dirents2. These aren't crawled
       * by this walker so we have to crawl them separately. */
      if (depth > svn_depth_files &&
          dirent2->kind == svn_node_dir &&
          (dirent1->kind == svn_node_file || dirent1->kind == svn_node_none))
        SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath,
                                       b->root1_abspath, b->root2_abspath,
                                       depth <= svn_depth_immediates
                                         ? svn_depth_empty
                                         : svn_depth_infinity ,
                                       b->callbacks, b->diff_baton,
                                       b->ctx, iterpool));
    }

  svn_pool_destroy(iterpool);

  return SVN_NO_ERROR;
}
Exemplo n.º 19
0
svn_error_t *
svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot,
                                      const char **local_relpath,
                                      svn_wc__db_t *db,
                                      const char *local_abspath,
                                      apr_pool_t *result_pool,
                                      apr_pool_t *scratch_pool)
{
  const char *local_dir_abspath;
  const char *original_abspath = local_abspath;
  svn_node_kind_t kind;
  const char *build_relpath;
  svn_wc__db_wcroot_t *probe_wcroot;
  svn_wc__db_wcroot_t *found_wcroot = NULL;
  const char *scan_abspath;
  svn_sqlite__db_t *sdb = NULL;
  svn_boolean_t moved_upwards = FALSE;
  svn_boolean_t always_check = FALSE;
  int wc_format = 0;
  const char *adm_relpath;
  /* Non-NULL if WCROOT is found through a symlink: */
  const char *symlink_wcroot_abspath = NULL;

  /* ### we need more logic for finding the database (if it is located
     ### outside of the wcroot) and then managing all of that within DB.
     ### for now: play quick & dirty. */

  probe_wcroot = svn_hash_gets(db->dir_data, local_abspath);
  if (probe_wcroot != NULL)
    {
      *wcroot = probe_wcroot;

      /* We got lucky. Just return the thing BEFORE performing any I/O.  */
      /* ### validate SMODE against how we opened wcroot->sdb? and against
         ### DB->mode? (will we record per-dir mode?)  */

      /* ### for most callers, we could pass NULL for result_pool.  */
      *local_relpath = compute_relpath(probe_wcroot, local_abspath,
                                       result_pool);

      return SVN_NO_ERROR;
    }

  /* ### at some point in the future, we may need to find a way to get
     ### rid of this stat() call. it is going to happen for EVERY call
     ### into wc_db which references a file. calls for directories could
     ### get an early-exit in the hash lookup just above.  */
  SVN_ERR(get_path_kind(&kind, db, local_abspath, scratch_pool));
  if (kind != svn_node_dir)
    {
      /* If the node specified by the path is NOT present, then it cannot
         possibly be a directory containing ".svn/wc.db".

         If it is a file, then it cannot contain ".svn/wc.db".

         For both of these cases, strip the basename off of the path and
         move up one level. Keep record of what we strip, though, since
         we'll need it later to construct local_relpath.  */
      svn_dirent_split(&local_dir_abspath, &build_relpath, local_abspath,
                       scratch_pool);

      /* Is this directory in our hash?  */
      probe_wcroot = svn_hash_gets(db->dir_data, local_dir_abspath);
      if (probe_wcroot != NULL)
        {
          const char *dir_relpath;

          *wcroot = probe_wcroot;

          /* Stashed directory's local_relpath + basename. */
          dir_relpath = compute_relpath(probe_wcroot, local_dir_abspath,
                                        NULL);
          *local_relpath = svn_relpath_join(dir_relpath,
                                            build_relpath,
                                            result_pool);
          return SVN_NO_ERROR;
        }

      /* If the requested path is not on the disk, then we don't know how
         many ancestors need to be scanned until we start hitting content
         on the disk. Set ALWAYS_CHECK to keep looking for .svn/entries
         rather than bailing out after the first check.  */
      if (kind == svn_node_none)
        always_check = TRUE;

      /* Start the scanning at LOCAL_DIR_ABSPATH.  */
      local_abspath = local_dir_abspath;
    }
  else
    {
      /* Start the local_relpath empty. If *this* directory contains the
         wc.db, then relpath will be the empty string.  */
      build_relpath = "";

      /* Remember the dir containing LOCAL_ABSPATH (they're the same).  */
      local_dir_abspath = local_abspath;
    }

  /* LOCAL_ABSPATH refers to a directory at this point. At this point,
     we've determined that an associated WCROOT is NOT in the DB's hash
     table for this directory. Let's find an existing one in the ancestors,
     or create one when we find the actual wcroot.  */

  /* Assume that LOCAL_ABSPATH is a directory, and look for the SQLite
     database in the right place. If we find it... great! If not, then
     peel off some components, and try again. */

  adm_relpath = svn_wc_get_adm_dir(scratch_pool);
  while (TRUE)
    {
      svn_error_t *err;
      svn_node_kind_t adm_subdir_kind;

      const char *adm_subdir = svn_dirent_join(local_abspath, adm_relpath,
                                               scratch_pool);

      SVN_ERR(svn_io_check_path(adm_subdir, &adm_subdir_kind, scratch_pool));

      if (adm_subdir_kind == svn_node_dir)
        {
          /* We always open the database in read/write mode.  If the database
             isn't writable in the filesystem, SQLite will internally open
             it as read-only, and we'll get an error if we try to do a write
             operation.

             We could decide what to do on a per-operation basis, but since
             we're caching database handles, it make sense to be as permissive
             as the filesystem allows. */
          err = svn_wc__db_util_open_db(&sdb, local_abspath, SDB_FILE,
                                        svn_sqlite__mode_readwrite,
                                        db->exclusive, db->timeout, NULL,
                                        db->state_pool, scratch_pool);
          if (err == NULL)
            {
#ifdef SVN_DEBUG
              /* Install self-verification trigger statements. */
              err = svn_sqlite__exec_statements(sdb,
                                                STMT_VERIFICATION_TRIGGERS);
              if (err && err->apr_err == SVN_ERR_SQLITE_ERROR)
                {
                  /* Verification triggers can fail to install on old 1.7-dev
                   * formats which didn't have a NODES table yet. Ignore sqlite
                   * errors so such working copies can be upgraded. */
                  svn_error_clear(err);
                }
              else
                SVN_ERR(err);
#endif
              break;
            }
          if (err->apr_err != SVN_ERR_SQLITE_ERROR
              && !APR_STATUS_IS_ENOENT(err->apr_err))
            return svn_error_trace(err);
          svn_error_clear(err);

          /* If we have not moved upwards, then check for a wc-1 working copy.
             Since wc-1 has a .svn in every directory, and we didn't find one
             in the original directory, then we aren't looking at a wc-1.

             If the original path is not present, then we have to check on every
             iteration. The content may be the immediate parent, or possibly
             five ancetors higher. We don't test for directory presence (just
             for the presence of subdirs/files), so we don't know when we can
             stop checking ... so just check always.  */
          if (!moved_upwards || always_check)
            {
              SVN_ERR(get_old_version(&wc_format, local_abspath,
                                      scratch_pool));
              if (wc_format != 0)
                break;
            }
        }

      /* We couldn't open the SDB within the specified directory, so
         move up one more directory. */
      if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
        {
          /* Hit the root without finding a wcroot. */

          /* The wcroot could be a symlink to a directory.
           * (Issue #2557, #3987). If so, try again, this time scanning
           * for a db within the directory the symlink points to,
           * rather than within the symlink's parent directory. */
          if (kind == svn_node_symlink)
            {
              svn_node_kind_t resolved_kind;

              local_abspath = original_abspath;

              SVN_ERR(svn_io_check_resolved_path(local_abspath,
                                                 &resolved_kind,
                                                 scratch_pool));
              if (resolved_kind == svn_node_dir)
                {
                  /* Is this directory recorded in our hash?  */
                  found_wcroot = svn_hash_gets(db->dir_data, local_abspath);
                  if (found_wcroot)
                    break;

                  symlink_wcroot_abspath = local_abspath;
                  SVN_ERR(read_link_target(&local_abspath, local_abspath,
                                           scratch_pool));
try_symlink_as_dir:
                  kind = svn_node_dir;
                  moved_upwards = FALSE;
                  local_dir_abspath = local_abspath;
                  build_relpath = "";

                  continue;
                }
            }

          return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
                                   _("'%s' is not a working copy"),
                                   svn_dirent_local_style(original_abspath,
                                                          scratch_pool));
        }

      local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);

      moved_upwards = TRUE;
      symlink_wcroot_abspath = NULL;

      /* Is the parent directory recorded in our hash?  */
      found_wcroot = svn_hash_gets(db->dir_data, local_abspath);
      if (found_wcroot != NULL)
        break;
    }

  if (found_wcroot != NULL)
    {
      /* We found a hash table entry for an ancestor, so we stopped scanning
         since all subdirectories use the same WCROOT.  */
      *wcroot = found_wcroot;
    }
  else if (wc_format == 0)
    {
      /* We finally found the database. Construct a wcroot_t for it.  */

      apr_int64_t wc_id;
      int format;
      svn_error_t *err;

      err = fetch_sdb_info(&wc_id, &format, sdb, scratch_pool);
      if (err)
        {
          if (err->apr_err == SVN_ERR_WC_CORRUPT)
            return svn_error_quick_wrapf(
              err, _("Missing a row in WCROOT for '%s'."),
              svn_dirent_local_style(original_abspath, scratch_pool));
          return svn_error_trace(err);
        }

      /* WCROOT.local_abspath may be NULL when the database is stored
         inside the wcroot, but we know the abspath is this directory
         (ie. where we found it).  */

      err = svn_wc__db_pdh_create_wcroot(wcroot,
                            apr_pstrdup(db->state_pool,
                                        symlink_wcroot_abspath
                                          ? symlink_wcroot_abspath
                                          : local_abspath),
                            sdb, wc_id, format,
                            db->verify_format,
                            db->state_pool, scratch_pool);
      if (err && (err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT ||
                  err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) &&
          kind == svn_node_symlink)
        {
          /* We found an unsupported WC after traversing upwards from a
           * symlink. Fall through to code below to check if the symlink
           * points at a supported WC. */
          svn_error_clear(err);
          *wcroot = NULL;
        }
      else if (err)
        {
          /* Close handle if we are not going to use it to support
             upgrading with exclusive wc locking. */
          return svn_error_compose_create(err, svn_sqlite__close(sdb));
        }
    }
  else
    {
      /* We found something that looks like a wc-1 working copy directory.
         However, if the format version is 12 and the .svn/entries file
         is only 3 bytes long, then it's a breadcrumb in a wc-ng working
         copy that's missing an .svn/wc.db, or its .svn/wc.db is corrupt. */
      if (wc_format == SVN_WC__WC_NG_VERSION /* 12 */)
        {
          apr_finfo_t info;

          /* Check attributes of .svn/entries */
          const char *admin_abspath = svn_wc__adm_child(
              local_abspath, SVN_WC__ADM_ENTRIES, scratch_pool);
          svn_error_t *err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE,
                                         scratch_pool);

          /* If the former does not succeed, something is seriously wrong. */
          if (err)
            return svn_error_createf(
                SVN_ERR_WC_CORRUPT, err,
                _("The working copy at '%s' is corrupt."),
                svn_dirent_local_style(local_abspath, scratch_pool));
          svn_error_clear(err);

          if (3 == info.size)
            {
              /* Check existence of .svn/wc.db */
              admin_abspath = svn_wc__adm_child(local_abspath, SDB_FILE,
                                                scratch_pool);
              err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE,
                                scratch_pool);
              if (err && APR_STATUS_IS_ENOENT(err->apr_err))
                {
                  svn_error_clear(err);
                  return svn_error_createf(
                      SVN_ERR_WC_CORRUPT, NULL,
                      _("The working copy database at '%s' is missing."),
                      svn_dirent_local_style(local_abspath, scratch_pool));
                }
              else
                /* We should never have reached this point in the code
                   if .svn/wc.db exists; therefore it's best to assume
                   it's corrupt. */
                return svn_error_createf(
                    SVN_ERR_WC_CORRUPT, err,
                    _("The working copy database at '%s' is corrupt."),
                    svn_dirent_local_style(local_abspath, scratch_pool));
            }
        }

      SVN_ERR(svn_wc__db_pdh_create_wcroot(wcroot,
                            apr_pstrdup(db->state_pool,
                                        symlink_wcroot_abspath
                                          ? symlink_wcroot_abspath
                                          : local_abspath),
                            NULL, UNKNOWN_WC_ID, wc_format,
                            db->verify_format,
                            db->state_pool, scratch_pool));
    }

  if (*wcroot)
    {
      const char *dir_relpath;

      if (symlink_wcroot_abspath)
        {
          /* The WCROOT was found through a symlink pointing at the root of
           * the WC. Cache the WCROOT under the symlink's path. */
          local_dir_abspath = symlink_wcroot_abspath;
        }

      /* The subdirectory's relpath is easily computed relative to the
         wcroot that we just found.  */
      dir_relpath = compute_relpath(*wcroot, local_dir_abspath, NULL);

      /* And the result local_relpath may include a filename.  */
      *local_relpath = svn_relpath_join(dir_relpath, build_relpath, result_pool);
    }

  if (kind == svn_node_symlink)
    {
      svn_boolean_t retry_if_dir = FALSE;
      svn_wc__db_status_t status;
      svn_boolean_t conflicted;
      svn_error_t *err;

      /* Check if the symlink is versioned or obstructs a versioned node
       * in this DB -- in that case, use this wcroot. Else, if the symlink
       * points to a directory, try to find a wcroot in that directory
       * instead. */

      if (*wcroot)
        {
          err = svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL,
                                              NULL, NULL, NULL, NULL, NULL,
                                              NULL, NULL, NULL, NULL, NULL,
                                              NULL, NULL, NULL, &conflicted,
                                              NULL, NULL, NULL, NULL, NULL,
                                              NULL, *wcroot, *local_relpath,
                                              scratch_pool, scratch_pool);
          if (err)
            {
              if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
                  && !SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
                return svn_error_trace(err);

              svn_error_clear(err);
              retry_if_dir = TRUE; /* The symlink is unversioned. */
            }
          else
            {
              /* The symlink is versioned, or obstructs a versioned node.
               * Ignore non-conflicted not-present/excluded nodes.
               * This allows the symlink to redirect the wcroot query to a
               * directory, regardless of 'invisible' nodes in this WC. */
              retry_if_dir = ((status == svn_wc__db_status_not_present ||
                               status == svn_wc__db_status_excluded ||
                               status == svn_wc__db_status_server_excluded)
                              && !conflicted);
            }
        }
      else
        retry_if_dir = TRUE;

      if (retry_if_dir)
        {
          svn_node_kind_t resolved_kind;

          SVN_ERR(svn_io_check_resolved_path(original_abspath,
                                             &resolved_kind,
                                             scratch_pool));
          if (resolved_kind == svn_node_dir)
            {
              symlink_wcroot_abspath = original_abspath;
              SVN_ERR(read_link_target(&local_abspath, original_abspath,
                                       scratch_pool));
              /* This handle was opened in this function but is not going
                 to be used further so close it. */
              if (sdb)
                SVN_ERR(svn_sqlite__close(sdb));
              goto try_symlink_as_dir;
            }
        }
    }

  /* We've found the appropriate WCROOT for the requested path. Stash
     it into that path's directory.  */
  svn_hash_sets(db->dir_data,
                apr_pstrdup(db->state_pool, local_dir_abspath),
                *wcroot);

  /* Did we traverse up to parent directories?  */
  if (!moved_upwards)
    {
      /* We did NOT move to a parent of the original requested directory.
         We've constructed and filled in a WCROOT for the request, so we
         are done.  */
      return SVN_NO_ERROR;
    }

  /* The WCROOT that we just found/built was for the LOCAL_ABSPATH originally
     passed into this function. We stepped *at least* one directory above that.
     We should now associate the WROOT for each parent directory that does
     not (yet) have one.  */

  scan_abspath = local_dir_abspath;

  do
    {
      const char *parent_dir = svn_dirent_dirname(scan_abspath, scratch_pool);
      svn_wc__db_wcroot_t *parent_wcroot;

      parent_wcroot = svn_hash_gets(db->dir_data, parent_dir);
      if (parent_wcroot == NULL)
        {
          svn_hash_sets(db->dir_data, apr_pstrdup(db->state_pool, parent_dir),
                        *wcroot);
        }

      /* Move up a directory, stopping when we reach the directory where
         we found/built the WCROOT.  */
      scan_abspath = parent_dir;
    }
  while (strcmp(scan_abspath, local_abspath) != 0);

  return SVN_NO_ERROR;
}
Exemplo n.º 20
0
static svn_error_t *
mkdir_urls(const apr_array_header_t *urls,
           svn_boolean_t make_parents,
           const apr_hash_t *revprop_table,
           svn_commit_callback2_t commit_callback,
           void *commit_baton,
           svn_client_ctx_t *ctx,
           apr_pool_t *pool)
{
  svn_ra_session_t *ra_session = NULL;
  const svn_delta_editor_t *editor;
  void *edit_baton;
  const char *log_msg;
  apr_array_header_t *targets;
  apr_hash_t *targets_hash;
  apr_hash_t *commit_revprops;
  svn_error_t *err;
  const char *common;
  int i;

  /* Find any non-existent parent directories */
  if (make_parents)
    {
      apr_array_header_t *all_urls = apr_array_make(pool, urls->nelts,
                                                    sizeof(const char *));
      const char *first_url = APR_ARRAY_IDX(urls, 0, const char *);
      apr_pool_t *iterpool = svn_pool_create(pool);

      SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL,
                                                   first_url, NULL, NULL,
                                                   FALSE, TRUE, ctx, pool));

      for (i = 0; i < urls->nelts; i++)
        {
          const char *url = APR_ARRAY_IDX(urls, i, const char *);

          svn_pool_clear(iterpool);
          SVN_ERR(add_url_parents(ra_session, url, all_urls, iterpool, pool));
        }

      svn_pool_destroy(iterpool);

      urls = all_urls;
    }

  /* Condense our list of mkdir targets. */
  SVN_ERR(svn_uri_condense_targets(&common, &targets, urls, FALSE,
                                   pool, pool));

  /*Remove duplicate targets introduced by make_parents with more targets. */
  SVN_ERR(svn_hash_from_cstring_keys(&targets_hash, targets, pool));
  SVN_ERR(svn_hash_keys(&targets, targets_hash, pool));

  if (! targets->nelts)
    {
      const char *bname;
      svn_uri_split(&common, &bname, common, pool);
      APR_ARRAY_PUSH(targets, const char *) = bname;

      if (*bname == '\0')
        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
                                 _("There is no valid uri above '%s'"),
                                 common);
    }
  else
    {
      svn_boolean_t resplit = FALSE;

      /* We can't "mkdir" the root of an editor drive, so if one of
         our targets is the empty string, we need to back everything
         up by a path component. */
      for (i = 0; i < targets->nelts; i++)
        {
          const char *path = APR_ARRAY_IDX(targets, i, const char *);
          if (! *path)
            {
              resplit = TRUE;
              break;
            }
        }
      if (resplit)
        {
          const char *bname;

          svn_uri_split(&common, &bname, common, pool);

          if (*bname == '\0')
             return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
                                      _("There is no valid uri above '%s'"),
                                      common);

          for (i = 0; i < targets->nelts; i++)
            {
              const char *path = APR_ARRAY_IDX(targets, i, const char *);
              path = svn_relpath_join(bname, path, pool);
              APR_ARRAY_IDX(targets, i, const char *) = path;
            }
        }
    }
  qsort(targets->elts, targets->nelts, targets->elt_size,
        svn_sort_compare_paths);

  /* ### This reparent may be problematic in limited-authz-to-common-parent
     ### scenarios (compare issue #3242).  See also issue #3649. */
  if (ra_session)
    SVN_ERR(svn_ra_reparent(ra_session, common, pool));

  /* Create new commit items and add them to the array. */
  if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
    {
      svn_client_commit_item3_t *item;
      const char *tmp_file;
      apr_array_header_t *commit_items
        = apr_array_make(pool, targets->nelts, sizeof(item));

      for (i = 0; i < targets->nelts; i++)
        {
          const char *path = APR_ARRAY_IDX(targets, i, const char *);

          item = svn_client_commit_item3_create(pool);
          item->url = svn_path_url_add_component2(common, path, pool);
          item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
          APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
        }

      SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
                                      ctx, pool));

      if (! log_msg)
        return SVN_NO_ERROR;
    }
  else
Exemplo n.º 21
0
/* ### TODO: Handle depth. */
static svn_error_t *
diff_deleted_dir(svn_wc_notify_state_t *state_p,
                 svn_boolean_t *tree_conflicted_p,
                 const char *dir,
                 struct edit_baton *eb,
                 apr_pool_t *pool)
{
  apr_hash_t *dirents;
  apr_pool_t *iterpool = svn_pool_create(pool);
  apr_hash_index_t *hi;

  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(eb->revision));

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

  SVN_ERR(eb->diff_callbacks->dir_deleted(
                        state_p, tree_conflicted_p, dir,
                        eb->diff_cmd_baton, pool));

  /* The "old" dir will be skipped by the repository report.  If required,
   * crawl it recursively, diffing each file against the empty file.  This
   * is a workaround for issue 2333 "'svn diff URL1 URL2' not reverse of
   * 'svn diff URL2 URL1'". */
  if (! eb->walk_deleted_repos_dirs)
    {
      svn_pool_destroy(iterpool);
      return SVN_NO_ERROR;
    }

  SVN_ERR(svn_ra_get_dir2(eb->ra_session,
                          &dirents,
                          NULL, NULL,
                          dir,
                          eb->revision,
                          SVN_DIRENT_KIND,
                          pool));

  for (hi = apr_hash_first(pool, dirents); hi;
       hi = apr_hash_next(hi))
    {
      const char *path;
      const char *name = svn__apr_hash_index_key(hi);
      svn_dirent_t *dirent = svn__apr_hash_index_val(hi);

      svn_pool_clear(iterpool);

      path = svn_relpath_join(dir, name, iterpool);

      if (dirent->kind == svn_node_file)
        {
          SVN_ERR(diff_deleted_file(NULL, NULL, path, eb, iterpool));
        }

      if (dirent->kind == svn_node_dir)
        {
          SVN_ERR(diff_deleted_dir(NULL, NULL, path, eb, iterpool));
        }
    }

  svn_pool_destroy(iterpool);
  return SVN_NO_ERROR;
}