Beispiel #1
0
/* Getting just one file. */
static svn_error_t *
svn_ra_local__get_file(svn_ra_session_t *session,
                       const char *path,
                       svn_revnum_t revision,
                       svn_stream_t *stream,
                       svn_revnum_t *fetched_rev,
                       apr_hash_t **props,
                       apr_pool_t *pool)
{
  svn_fs_root_t *root;
  svn_stream_t *contents;
  svn_revnum_t youngest_rev;
  svn_ra_local__session_baton_t *sess = session->priv;
  const char *abs_path = svn_path_join(sess->fs_path->data, path, pool);

  /* Open the revision's root. */
  if (! SVN_IS_VALID_REVNUM(revision))
    {
      SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, pool));
      SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, pool));
      if (fetched_rev != NULL)
        *fetched_rev = youngest_rev;
    }
  else
    SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));

  if (stream)
    {
      /* Get a stream representing the file's contents. */
      SVN_ERR(svn_fs_file_contents(&contents, root, abs_path, pool));

      /* Now push data from the fs stream back at the caller's stream.
         Note that this particular RA layer does not computing a
         checksum as we go, and confirming it against the repository's
         checksum when done.  That's because it calls
         svn_fs_file_contents() directly, which already checks the
         stored checksum, and all we're doing here is writing bytes in
         a loop.  Truly, Nothing Can Go Wrong :-).  But RA layers that
         go over a network should confirm the checksum.

         Note: we are not supposed to close the passed-in stream, so
         disown the thing.
      */
      SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(stream, pool),
                               sess->callbacks
                                 ? sess->callbacks->cancel_func : NULL,
                               sess->callback_baton,
                               pool));
    }

  /* Handle props if requested. */
  if (props)
    SVN_ERR(get_node_props(props, sess, root, abs_path, pool));

  return SVN_NO_ERROR;
}
Beispiel #2
0
static PyObject *fs_get_revision_root(FileSystemObject *self, PyObject *args)
{
	svn_revnum_t rev;
	FileSystemRootObject *ret;
	apr_pool_t *pool;
	svn_fs_root_t *root;

	if (!PyArg_ParseTuple(args, "l", &rev))
		return NULL;

	pool = Pool(NULL);
	if (pool == NULL)
		return NULL;

	RUN_SVN_WITH_POOL(pool, svn_fs_revision_root(&root, self->fs, rev, pool));

	ret = PyObject_New(FileSystemRootObject, &FileSystemRoot_Type);
	if (ret == NULL)
		return NULL;

	ret->root = root;
	ret->pool = pool;

	return (PyObject *)ret;
}
Beispiel #3
0
static svn_error_t *
fetch_props_func(apr_hash_t **props,
                 void *baton,
                 const char *path,
                 svn_revnum_t base_revision,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool)
{
  struct edit_baton *eb = baton;
  svn_fs_root_t *fs_root;
  svn_error_t *err;

  SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs,
                               svn_fs_txn_base_revision(eb->txn),
                               scratch_pool));
  err = svn_fs_node_proplist(props, fs_root, path, result_pool);
  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
    {
      svn_error_clear(err);
      *props = apr_hash_make(result_pool);
      return SVN_NO_ERROR;
    }
  else if (err)
    return svn_error_trace(err);

  return SVN_NO_ERROR;
}
Beispiel #4
0
/* This implements svn_editor_cb_copy_t */
static svn_error_t *
copy_cb(void *baton,
        const char *src_relpath,
        svn_revnum_t src_revision,
        const char *dst_relpath,
        svn_revnum_t replaces_rev,
        apr_pool_t *scratch_pool)
{
    struct edit_baton *eb = baton;
    const char *src_fspath = FSPATH(src_relpath, scratch_pool);
    const char *dst_fspath = FSPATH(dst_relpath, scratch_pool);
    svn_fs_root_t *root;
    svn_fs_root_t *src_root;

    SVN_ERR(get_root(&root, eb));

    /* Check if we can we replace the maybe-specified destination (revision).  */
    if (SVN_IS_VALID_REVNUM(replaces_rev))
    {
        SVN_ERR(can_modify(root, dst_fspath, replaces_rev, scratch_pool));
        SVN_ERR(svn_fs_delete(root, dst_fspath, scratch_pool));
    }
    else
    {
        SVN_ERR(can_create(root, dst_fspath, scratch_pool));
    }

    SVN_ERR(svn_fs_revision_root(&src_root, svn_fs_root_fs(root), src_revision,
                                 scratch_pool));
    SVN_ERR(svn_fs_copy(src_root, src_fspath, root, dst_fspath, scratch_pool));
    svn_fs_close_root(src_root);

    return SVN_NO_ERROR;
}
Beispiel #5
0
svn_error_t *
svn_repos_fs_get_locks(apr_hash_t **locks,
                       svn_repos_t *repos,
                       const char *path,
                       svn_repos_authz_func_t authz_read_func,
                       void *authz_read_baton,
                       apr_pool_t *pool)
{
  apr_hash_t *all_locks = apr_hash_make(pool);
  svn_revnum_t head_rev;
  struct get_locks_baton_t baton;

  /* Locks are always said to apply to HEAD revision, so we'll check
     to see if locked-paths are readable in HEAD as well. */
  SVN_ERR(svn_fs_youngest_rev(&head_rev, repos->fs, pool));

  /* Populate our callback baton. */
  baton.fs = repos->fs;
  baton.locks = all_locks;
  baton.authz_read_func = authz_read_func;
  baton.authz_read_baton = authz_read_baton;
  SVN_ERR(svn_fs_revision_root(&(baton.head_root), repos->fs,
                               head_rev, pool));

  /* Get all the locks. */
  SVN_ERR(svn_fs_get_locks(repos->fs, path, get_locks_callback,
                           &baton, pool));

  *locks = baton.locks;
  return SVN_NO_ERROR;
}
Beispiel #6
0
svn_error_t *
svn_repos_fs_get_locks2(apr_hash_t **locks,
                        svn_repos_t *repos,
                        const char *path,
                        svn_depth_t depth,
                        svn_repos_authz_func_t authz_read_func,
                        void *authz_read_baton,
                        apr_pool_t *pool)
{
    apr_hash_t *all_locks = apr_hash_make(pool);
    svn_revnum_t head_rev;
    struct get_locks_baton_t baton;

    SVN_ERR_ASSERT((depth == svn_depth_empty) ||
                   (depth == svn_depth_files) ||
                   (depth == svn_depth_immediates) ||
                   (depth == svn_depth_infinity));

    SVN_ERR(svn_fs_youngest_rev(&head_rev, repos->fs, pool));

    /* Populate our callback baton. */
    baton.fs = repos->fs;
    baton.locks = all_locks;
    baton.authz_read_func = authz_read_func;
    baton.authz_read_baton = authz_read_baton;
    SVN_ERR(svn_fs_revision_root(&(baton.head_root), repos->fs,
                                 head_rev, pool));

    /* Get all the locks. */
    SVN_ERR(svn_fs_get_locks2(repos->fs, path, depth,
                              get_locks_callback, &baton, pool));

    *locks = baton.locks;
    return SVN_NO_ERROR;
}
Beispiel #7
0
 void set_revision(svn_revnum_t revnum)
   {
   if (revnum == 0)
     {
     check_svn(svn_fs_youngest_rev(&revnum, fs, apr_pool));
     }
   check_svn(svn_fs_revision_root(&fs_root, fs, revnum, apr_pool));
   }
Beispiel #8
0
 Repository(char const* path)
   {
   svn_repos_t *repos;
   check_svn(svn_repos_open(&repos, path, apr_pool));
   fs = svn_repos_fs(repos);
   svn_revnum_t revnum;
   check_svn(svn_fs_youngest_rev(&revnum, fs, apr_pool));
   check_svn(svn_fs_revision_root(&fs_root, fs, revnum, apr_pool));
   }
Beispiel #9
0
svn_error_t *
svn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo,
                           svn_repos_t *repos,
                           const apr_array_header_t *paths,
                           svn_revnum_t rev,
                           svn_mergeinfo_inheritance_t inherit,
                           svn_boolean_t include_descendants,
                           svn_repos_authz_func_t authz_read_func,
                           void *authz_read_baton,
                           apr_pool_t *pool)
{
    /* Here we cast away 'const', but won't try to write through this pointer
     * without first allocating a new array. */
    apr_array_header_t *readable_paths = (apr_array_header_t *) paths;
    svn_fs_root_t *root;
    apr_pool_t *iterpool = svn_pool_create(pool);

    if (!SVN_IS_VALID_REVNUM(rev))
        SVN_ERR(svn_fs_youngest_rev(&rev, repos->fs, pool));
    SVN_ERR(svn_fs_revision_root(&root, repos->fs, rev, pool));

    /* Filter out unreadable paths before divining merge tracking info. */
    if (authz_read_func)
    {
        int i;

        for (i = 0; i < paths->nelts; i++)
        {
            svn_boolean_t readable;
            const char *path = APR_ARRAY_IDX(paths, i, char *);
            svn_pool_clear(iterpool);
            SVN_ERR(authz_read_func(&readable, root, path, authz_read_baton,
                                    iterpool));
            if (readable && readable_paths != paths)
                APR_ARRAY_PUSH(readable_paths, const char *) = path;
            else if (!readable && readable_paths == paths)
            {
                /* Requested paths differ from readable paths.  Fork
                   list of readable paths from requested paths. */
                int j;
                readable_paths = apr_array_make(pool, paths->nelts - 1,
                                                sizeof(char *));
                for (j = 0; j < i; j++)
                {
                    path = APR_ARRAY_IDX(paths, j, char *);
                    APR_ARRAY_PUSH(readable_paths, const char *) = path;
                }
            }
        }
    }
Beispiel #10
0
static svn_error_t *
delete_entry(const char *path,
             svn_revnum_t revision,
             void *parent_baton,
             apr_pool_t *pool)
{
  struct node_baton *d = parent_baton;
  struct edit_baton *eb = d->edit_baton;
  svn_repos_node_t *node;
  const char *name;
  const char *base_path;
  svn_revnum_t base_rev;
  svn_fs_root_t *base_root;
  svn_node_kind_t kind;

  /* Get (or create) the change node and update it. */
  name = svn_path_basename(path, pool);
  node = find_child_by_name(d->node, name);
  if (! node)
    node = create_child_node(d->node, name, eb->node_pool);
  node->action = 'D';

  /* We need to look up this node's parents to see what its original
     path in the filesystem was.  Why?  Because if this deletion
     occurred underneath a copied path, the thing that was deleted
     probably lived at a different location (relative to the copy
     source). */
  find_real_base_location(&base_path, &base_rev, node, pool);
  if (! SVN_IS_VALID_REVNUM(base_rev))
    {
      /* No interesting base revision?  We'll just look for the path
         in our base root.  */
      base_root = eb->base_root;
    }
  else
    {
      /* Oh.  Perhaps some copy goodness happened somewhere? */
      SVN_ERR(svn_fs_revision_root(&base_root, eb->fs, base_rev, pool));
    }

  /* Now figure out if this thing was a file or a dir. */
  SVN_ERR(svn_fs_check_path(&kind, base_root, base_path, pool));
  if (kind == svn_node_none)
    return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
                             _("'%s' not found in filesystem"), path);
  node->kind = kind;

  return SVN_NO_ERROR;
}
Beispiel #11
0
static svn_error_t *
svn_ra_local__do_check_path(svn_ra_session_t *session,
                            const char *path,
                            svn_revnum_t revision,
                            svn_node_kind_t *kind,
                            apr_pool_t *pool)
{
  svn_ra_local__session_baton_t *sess = session->priv;
  svn_fs_root_t *root;
  const char *abs_path = svn_path_join(sess->fs_path->data, path, pool);

  if (! SVN_IS_VALID_REVNUM(revision))
    SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, pool));
  SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
  return svn_fs_check_path(kind, root, abs_path, pool);
}
Beispiel #12
0
static svn_error_t *
get_dump_editor(const svn_delta_editor_t **editor,
                void **edit_baton,
                svn_fs_t *fs,
                svn_revnum_t to_rev,
                const char *root_path,
                svn_stream_t *stream,
                svn_repos_notify_func_t notify_func,
                void *notify_baton,
                svn_revnum_t oldest_dumped_rev,
                svn_boolean_t use_deltas,
                svn_boolean_t verify,
                apr_pool_t *pool)
{
  /* Allocate an edit baton to be stored in every directory baton.
     Set it up for the directory baton we create here, which is the
     root baton. */
  struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
  svn_delta_editor_t *dump_editor = svn_delta_default_editor(pool);

  /* Set up the edit baton. */
  eb->stream = stream;
  eb->notify_func = notify_func;
  eb->notify_baton = notify_baton;
  eb->oldest_dumped_rev = oldest_dumped_rev;
  eb->bufsize = sizeof(eb->buffer);
  eb->path = apr_pstrdup(pool, root_path);
  SVN_ERR(svn_fs_revision_root(&(eb->fs_root), fs, to_rev, pool));
  eb->current_rev = to_rev;
  eb->use_deltas = use_deltas;
  eb->verify = verify;

  /* Set up the editor. */
  dump_editor->open_root = open_root;
  dump_editor->delete_entry = delete_entry;
  dump_editor->add_directory = add_directory;
  dump_editor->open_directory = open_directory;
  dump_editor->close_directory = close_directory;
  dump_editor->change_dir_prop = change_dir_prop;
  dump_editor->add_file = add_file;
  dump_editor->open_file = open_file;

  *edit_baton = eb;
  *editor = dump_editor;

  return SVN_NO_ERROR;
}
static svn_error_t *
read_packed_fs(const char **msg,
               svn_boolean_t msg_only,
               svn_test_opts_t *opts,
               apr_pool_t *pool)
{
  svn_fs_t *fs;
  svn_stream_t *rstream;
  svn_stringbuf_t *rstring;
  svn_revnum_t i;

  *msg = "read from a packed FSFS filesystem";

  if (msg_only)
    return SVN_NO_ERROR;

  /* Bail (with success) on known-untestable scenarios */
  if ((strcmp(opts->fs_type, "fsfs") != 0)
      || (opts->server_minor_version && (opts->server_minor_version < 6)))
    return SVN_NO_ERROR;

  SVN_ERR(create_packed_filesystem(REPO_NAME, opts, 11, 5, pool));
  SVN_ERR(svn_fs_open(&fs, REPO_NAME, NULL, pool));

  for (i = 1; i < 12; i++)
    {
      svn_fs_root_t *rev_root;
      svn_stringbuf_t *sb;

      SVN_ERR(svn_fs_revision_root(&rev_root, fs, i, pool));
      SVN_ERR(svn_fs_file_contents(&rstream, rev_root, "iota", pool));
      SVN_ERR(svn_test__stream_to_string(&rstring, rstream, pool));

      if (i == 1)
        sb = svn_stringbuf_create("This is the file 'iota'.\n", pool);
      else
        sb = svn_stringbuf_create(get_rev_contents(i, pool), pool);

      if (! svn_stringbuf_compare(rstring, sb))
        return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
                                 "Bad data in revision %ld.", i);
    }

  return SVN_NO_ERROR;
}
Beispiel #14
0
static svn_error_t *
svn_ra_local__replay(svn_ra_session_t *session,
                     svn_revnum_t revision,
                     svn_revnum_t low_water_mark,
                     svn_boolean_t send_deltas,
                     const svn_delta_editor_t *editor,
                     void *edit_baton,
                     apr_pool_t *pool)
{
  svn_ra_local__session_baton_t *sess = session->priv;
  svn_fs_root_t *root;

  SVN_ERR(svn_fs_revision_root(&root, svn_repos_fs(sess->repos),
                               revision, pool));
  return svn_repos_replay2(root, sess->fs_path->data, low_water_mark,
                           send_deltas, editor, edit_baton, NULL, NULL,
                           pool);
}
Beispiel #15
0
static svn_error_t *
fetch_kind_func(svn_node_kind_t *kind,
                void *baton,
                const char *path,
                svn_revnum_t base_revision,
                apr_pool_t *scratch_pool)
{
  struct edit_baton *eb = baton;
  svn_fs_root_t *fs_root;

  if (!SVN_IS_VALID_REVNUM(base_revision))
    base_revision = svn_fs_txn_base_revision(eb->txn);

  SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs, base_revision, scratch_pool));

  SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool));

  return SVN_NO_ERROR;
}
/* Build the node-origins index any newly added items introduced in
   REVISION in FS.  Set *COUNT to the number of new items found.  */
static svn_error_t *
index_revision_adds(int *count, svn_fs_t *fs,
                    svn_revnum_t revision, apr_pool_t *pool)
{
  svn_fs_root_t *root;
  apr_hash_t *changes;
  apr_hash_index_t *hi;
  apr_pool_t *subpool;

  *count = 0;
  SVN_ERR(svn_fs_revision_root(&root, fs, revision, pool));
  SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));

  /* No paths changed in this revision?  Nothing to do.  */
  if (apr_hash_count(changes) == 0)
    return SVN_NO_ERROR;

  subpool = svn_pool_create(pool);
  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
    {
      const void *path;
      void *val;
      svn_fs_path_change2_t *change;

      svn_pool_clear(subpool);
      apr_hash_this(hi, &path, NULL, &val);
      change = val;
      if ((change->change_kind == svn_fs_path_change_add)
          || (change->change_kind == svn_fs_path_change_replace))
        {
          if (! (change->copyfrom_path
                            && SVN_IS_VALID_REVNUM(change->copyfrom_rev)))
            {
              svn_revnum_t origin;
              SVN_ERR(svn_fs_node_origin_rev(&origin, root, path, subpool));
              (*count)++;
            }
        }
    }
  svn_pool_destroy(subpool);

  return SVN_NO_ERROR;
}
Beispiel #17
0
/* This implements svn_editor_cb_move_t */
static svn_error_t *
move_cb(void *baton,
        const char *src_relpath,
        svn_revnum_t src_revision,
        const char *dst_relpath,
        svn_revnum_t replaces_rev,
        apr_pool_t *scratch_pool)
{
    struct edit_baton *eb = baton;
    const char *src_fspath = FSPATH(src_relpath, scratch_pool);
    const char *dst_fspath = FSPATH(dst_relpath, scratch_pool);
    svn_fs_root_t *root;
    svn_fs_root_t *src_root;

    SVN_ERR(get_root(&root, eb));

    /* Check if we delete the specified source (revision), and can we replace
       the maybe-specified destination (revision).  */
    SVN_ERR(can_modify(root, src_fspath, src_revision, scratch_pool));
    if (SVN_IS_VALID_REVNUM(replaces_rev))
    {
        SVN_ERR(can_modify(root, dst_fspath, replaces_rev, scratch_pool));
        SVN_ERR(svn_fs_delete(root, dst_fspath, scratch_pool));
    }
    else
    {
        SVN_ERR(can_create(root, dst_fspath, scratch_pool));
    }

    /* ### would be nice to have svn_fs_move()  */

    /* Copy the src to the dst. */
    SVN_ERR(svn_fs_revision_root(&src_root, svn_fs_root_fs(root), src_revision,
                                 scratch_pool));
    SVN_ERR(svn_fs_copy(src_root, src_fspath, root, dst_fspath, scratch_pool));
    svn_fs_close_root(src_root);

    /* Notice: we're deleting the src repos path from the dst root. */
    SVN_ERR(svn_fs_delete(root, src_fspath, scratch_pool));

    return SVN_NO_ERROR;
}
Beispiel #18
0
static svn_error_t *
test_open_file(const char *path,
               void *parent_baton,
               svn_revnum_t base_revision,
               apr_pool_t *file_pool,
               void **file_baton)
{
  struct dir_baton *pb = parent_baton;
  struct edit_baton *eb = pb->edit_baton;
  struct file_baton *fb = apr_pcalloc(file_pool, sizeof(*fb));
  svn_fs_root_t *rev_root = NULL;

  /* Fill in the file baton. */
  fb->path = svn_path_join(eb->root_path, path, eb->pool);
  fb->edit_baton = eb;

  SVN_ERR(svn_fs_revision_root(&rev_root, eb->fs, base_revision, file_pool));
  SVN_ERR(svn_fs_revision_link(rev_root, eb->txn_root, fb->path, file_pool));

  *file_baton = fb;
  return SVN_NO_ERROR;
}
Beispiel #19
0
static svn_error_t *
fetch_base_func(const char **filename,
                void *baton,
                const char *path,
                svn_revnum_t base_revision,
                apr_pool_t *result_pool,
                apr_pool_t *scratch_pool)
{
  struct edit_baton *eb = baton;
  svn_stream_t *contents;
  svn_stream_t *file_stream;
  const char *tmp_filename;
  svn_fs_root_t *fs_root;
  svn_error_t *err;

  if (!SVN_IS_VALID_REVNUM(base_revision))
    base_revision = svn_fs_txn_base_revision(eb->txn);

  SVN_ERR(svn_fs_revision_root(&fs_root, eb->fs, base_revision, scratch_pool));

  err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool);
  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
    {
      svn_error_clear(err);
      *filename = NULL;
      return SVN_NO_ERROR;
    }
  else if (err)
    return svn_error_trace(err);
  SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
                                 svn_io_file_del_on_pool_cleanup,
                                 scratch_pool, scratch_pool));
  SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));

  *filename = apr_pstrdup(result_pool, tmp_filename);

  return SVN_NO_ERROR;
}
Beispiel #20
0
static svn_error_t *
test_open_directory(const char *path,
                    void *parent_baton,
                    svn_revnum_t base_revision,
                    apr_pool_t *dir_pool,
                    void **child_baton)
{
  struct dir_baton *pb = parent_baton;
  struct edit_baton *eb = pb->edit_baton;
  struct dir_baton *db = apr_pcalloc(dir_pool, sizeof(*db));
  svn_fs_root_t *rev_root = NULL;

  /* Construct the full path of the new directory */
  db->full_path = svn_path_join(eb->root_path, path, eb->pool);
  db->edit_baton = eb;

  SVN_ERR(svn_fs_revision_root(&rev_root, eb->fs, base_revision, dir_pool));
  SVN_ERR(svn_fs_revision_link(rev_root, eb->txn_root, db->full_path, 
                               dir_pool));

  *child_baton = db;
  return SVN_NO_ERROR;
}
Beispiel #21
0
static svn_error_t *
test_add_file(const char *path,
              void *parent_baton,
              const char *copyfrom_path,
              svn_revnum_t copyfrom_revision,
              apr_pool_t *file_pool,
              void **file_baton)
{
  struct dir_baton *db = parent_baton;
  struct edit_baton *eb = db->edit_baton;
  struct file_baton *fb = apr_pcalloc(file_pool, sizeof(*fb));

  /* Fill in the file baton. */
  fb->path = svn_path_join(eb->root_path, path, eb->pool);
  fb->edit_baton = eb;

  if (copyfrom_path)  /* add with history */
    {
      svn_fs_root_t *rev_root = NULL;

      SVN_ERR(svn_fs_revision_root(&rev_root,
                                   eb->fs,
                                   copyfrom_revision,
                                   file_pool));

      SVN_ERR(svn_fs_copy(rev_root,
                          copyfrom_path,
                          eb->txn_root,
                          fb->path,
                          file_pool));
    }
  else  /* add without history */
    SVN_ERR(svn_fs_make_file(eb->txn_root, fb->path, file_pool));

  *file_baton = fb;
  return SVN_NO_ERROR;
}
Beispiel #22
0
static svn_error_t *
test_add_directory(const char *path,
                   void *parent_baton,
                   const char *copyfrom_path,
                   svn_revnum_t copyfrom_revision,
                   apr_pool_t *dir_pool,
                   void **child_baton)
{
  struct dir_baton *pb = parent_baton;
  struct edit_baton *eb = pb->edit_baton;
  struct dir_baton *db = apr_pcalloc(dir_pool, sizeof(*db));

  /* Construct the full path of the new directory */
  db->full_path = svn_path_join(eb->root_path, path, eb->pool);
  db->edit_baton = eb;

  if (copyfrom_path)  /* add with history */
    {
      svn_fs_root_t *rev_root = NULL;

      SVN_ERR(svn_fs_revision_root(&rev_root,
                                   eb->fs,
                                   copyfrom_revision,
                                   dir_pool));

      SVN_ERR(svn_fs_copy(rev_root,
                          copyfrom_path,
                          eb->txn_root,
                          db->full_path,
                          dir_pool));
    }
  else  /* add without history */
    SVN_ERR(svn_fs_make_dir(eb->txn_root, db->full_path, dir_pool));

  *child_baton = db;
  return SVN_NO_ERROR;
}
Beispiel #23
0
/* Perform a copy or a plain add.
 *
 * For a copy, also adjust the copy-from rev, check any copy-source checksum,
 * and send a notification.
 */
static svn_error_t *
maybe_add_with_history(struct node_baton *nb,
                       struct revision_baton *rb,
                       apr_pool_t *pool)
{
  struct parse_baton *pb = rb->pb;

  if ((nb->copyfrom_path == NULL) || (! pb->use_history))
    {
      /* Add empty file or dir, without history. */
      if (nb->kind == svn_node_file)
        SVN_ERR(svn_fs_make_file(rb->txn_root, nb->path, pool));

      else if (nb->kind == svn_node_dir)
        SVN_ERR(svn_fs_make_dir(rb->txn_root, nb->path, pool));
    }
  else
    {
      /* Hunt down the source revision in this fs. */
      svn_fs_root_t *copy_root;
      svn_revnum_t copyfrom_rev;

      /* Try to find the copyfrom revision in the revision map;
         failing that, fall back to the revision offset approach. */
      copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev);
      if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
        copyfrom_rev = nb->copyfrom_rev - rb->rev_offset;

      if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
        return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
                                 _("Relative source revision %ld is not"
                                   " available in current repository"),
                                 copyfrom_rev);

      SVN_ERR(svn_fs_revision_root(&copy_root, pb->fs, copyfrom_rev, pool));

      if (nb->copy_source_checksum)
        {
          svn_checksum_t *checksum;
          SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, copy_root,
                                       nb->copyfrom_path, TRUE, pool));
          if (!svn_checksum_match(nb->copy_source_checksum, checksum))
            return svn_checksum_mismatch_err(nb->copy_source_checksum,
                      checksum, pool,
                      _("Copy source checksum mismatch on copy from '%s'@%ld\n"
                        "to '%s' in rev based on r%ld"),
                      nb->copyfrom_path, copyfrom_rev, nb->path, rb->rev);
        }

      SVN_ERR(svn_fs_copy(copy_root, nb->copyfrom_path,
                          rb->txn_root, nb->path, pool));

      if (pb->notify_func)
        {
          /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
          svn_repos_notify_t *notify = svn_repos_notify_create(
                                            svn_repos_notify_load_copied_node,
                                            pb->notify_pool);

          pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
          svn_pool_clear(pb->notify_pool);
        }
    }

  return SVN_NO_ERROR;
}
Beispiel #24
0
dav_error *
dav_svn__merge_response(ap_filter_t *output,
                        const dav_svn_repos *repos,
                        svn_revnum_t new_rev,
                        char *post_commit_err,
                        apr_xml_elem *prop_elem,
                        svn_boolean_t disable_merge_response,
                        apr_pool_t *pool)
{
  apr_bucket_brigade *bb;
  svn_fs_root_t *root;
  svn_error_t *serr;
  const char *vcc;
  const char *rev;
  svn_string_t *creationdate, *creator_displayname;
  const char *post_commit_err_elem = NULL,
             *post_commit_header_info = NULL;

  serr = svn_fs_revision_root(&root, repos->fs, new_rev, pool);
  if (serr != NULL)
    {
      return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Could not open the FS root for the "
                                  "revision just committed.",
                                  repos->pool);
    }

  bb = apr_brigade_create(pool, output->c->bucket_alloc);

  /* prep some strings */

  /* the HREF for the baseline is actually the VCC */
  vcc = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_VCC, SVN_IGNORED_REVNUM,
                           NULL, 0 /* add_href */, pool);

  /* the version-name of the baseline is the revision number */
  rev = apr_psprintf(pool, "%ld", new_rev);

  /* get the post-commit hook stderr, if any */
  if (post_commit_err)
    {
      post_commit_header_info = apr_psprintf(pool,
                                             " xmlns:S=\"%s\"",
                                             SVN_XML_NAMESPACE);
      post_commit_err_elem = apr_psprintf(pool,
                                          "<S:post-commit-err>%s"
                                          "</S:post-commit-err>",
                                          post_commit_err);
    }
  else
    {
      post_commit_header_info = "" ;
      post_commit_err_elem = "" ;
    }


  /* get the creationdate and creator-displayname of the new revision, too. */
  serr = svn_fs_revision_prop(&creationdate, repos->fs, new_rev,
                              SVN_PROP_REVISION_DATE, pool);
  if (serr != NULL)
    {
      return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Could not get date of newest revision",
                                  repos->pool);
    }
  serr = svn_fs_revision_prop(&creator_displayname, repos->fs, new_rev,
                              SVN_PROP_REVISION_AUTHOR, pool);
  if (serr != NULL)
    {
      return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Could not get author of newest revision",
                                  repos->pool);
    }


  (void) ap_fputstrs(output, bb,
                     DAV_XML_HEADER DEBUG_CR
                     "<D:merge-response xmlns:D=\"DAV:\"",
                     post_commit_header_info,
                     ">" DEBUG_CR
                     "<D:updated-set>" DEBUG_CR

                     /* generate a response for the new baseline */
                     "<D:response>" DEBUG_CR
                     "<D:href>",
                     apr_xml_quote_string(pool, vcc, 1),
                     "</D:href>" DEBUG_CR
                     "<D:propstat><D:prop>" DEBUG_CR
                     /* ### this is wrong. it's a VCC, not a baseline. but
                        ### we need to tell the client to look at *this*
                        ### resource for the version-name. */
                     "<D:resourcetype><D:baseline/></D:resourcetype>" DEBUG_CR,
                     post_commit_err_elem, DEBUG_CR
                     "<D:version-name>", rev, "</D:version-name>" DEBUG_CR,
                     NULL);
  if (creationdate)
    {
      (void) ap_fputstrs(output, bb,
                         "<D:creationdate>",
                         apr_xml_quote_string(pool, creationdate->data, 1),
                         "</D:creationdate>" DEBUG_CR,
                         NULL);
    }
  if (creator_displayname)
    {
      (void) ap_fputstrs(output, bb,
                         "<D:creator-displayname>",
                         apr_xml_quote_string(pool,
                                              creator_displayname->data, 1),
                         "</D:creator-displayname>" DEBUG_CR,
                         NULL);
    }
  (void) ap_fputstrs(output, bb,
                     "</D:prop>" DEBUG_CR
                     "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
                     "</D:propstat>" DEBUG_CR
                     "</D:response>" DEBUG_CR,

                     NULL);

  /* ONLY have dir_delta drive the editor if the caller asked us to
     generate a full MERGE response.  svn clients can ask us to
     suppress this walk by sending specific request headers. */
  if (! disable_merge_response)
    {
      /* Now we need to generate responses for all the resources which
         changed.  This is done through a delta of the two roots.

         Note that a directory is not marked when open_dir is seen
         (since it typically is used just for changing members in that
         directory); instead, we want for a property change (the only
         reason the client would need to fetch a new directory).

         ### we probably should say something about the dirs, so that
         ### we can pass back the new version URL */

      /* and go make me proud, boy! */
      serr = do_resources(repos, root, new_rev, output, bb, pool);
      if (serr != NULL)
        {
          return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                      "Error constructing resource list.",
                                      repos->pool);
        }
    }

  /* wrap up the merge response */
  (void) ap_fputs(output, bb,
                  "</D:updated-set>" DEBUG_CR
                  "</D:merge-response>" DEBUG_CR);

  /* send whatever is left in the brigade */
  (void) ap_pass_brigade(output, bb);

  return SVN_NO_ERROR;
}
Beispiel #25
0
/* The main dumper. */
svn_error_t *
svn_repos_dump_fs3(svn_repos_t *repos,
                   svn_stream_t *stream,
                   svn_revnum_t start_rev,
                   svn_revnum_t end_rev,
                   svn_boolean_t incremental,
                   svn_boolean_t use_deltas,
                   svn_repos_notify_func_t notify_func,
                   void *notify_baton,
                   svn_cancel_func_t cancel_func,
                   void *cancel_baton,
                   apr_pool_t *pool)
{
  const svn_delta_editor_t *dump_editor;
  void *dump_edit_baton = NULL;
  svn_revnum_t i;
  svn_fs_t *fs = svn_repos_fs(repos);
  apr_pool_t *subpool = svn_pool_create(pool);
  svn_revnum_t youngest;
  const char *uuid;
  int version;
  svn_boolean_t found_old_reference = FALSE;
  svn_boolean_t found_old_mergeinfo = FALSE;
  svn_repos_notify_t *notify;

  /* Determine the current youngest revision of the filesystem. */
  SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));

  /* Use default vals if necessary. */
  if (! SVN_IS_VALID_REVNUM(start_rev))
    start_rev = 0;
  if (! SVN_IS_VALID_REVNUM(end_rev))
    end_rev = youngest;
  if (! stream)
    stream = svn_stream_empty(pool);

  /* Validate the revisions. */
  if (start_rev > end_rev)
    return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL,
                             _("Start revision %ld"
                               " is greater than end revision %ld"),
                             start_rev, end_rev);
  if (end_rev > youngest)
    return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL,
                             _("End revision %ld is invalid "
                               "(youngest revision is %ld)"),
                             end_rev, youngest);
  if ((start_rev == 0) && incremental)
    incremental = FALSE; /* revision 0 looks the same regardless of
                            whether or not this is an incremental
                            dump, so just simplify things. */

  /* Write out the UUID. */
  SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool));

  /* If we're not using deltas, use the previous version, for
     compatibility with svn 1.0.x. */
  version = SVN_REPOS_DUMPFILE_FORMAT_VERSION;
  if (!use_deltas)
    version--;

  /* Write out "general" metadata for the dumpfile.  In this case, a
     magic header followed by a dumpfile format version. */
  SVN_ERR(svn_stream_printf(stream, pool,
                            SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
                            version));
  SVN_ERR(svn_stream_printf(stream, pool, SVN_REPOS_DUMPFILE_UUID
                            ": %s\n\n", uuid));

  /* Create a notify object that we can reuse in the loop. */
  if (notify_func)
    notify = svn_repos_notify_create(svn_repos_notify_dump_rev_end,
                                     pool);

  /* Main loop:  we're going to dump revision i.  */
  for (i = start_rev; i <= end_rev; i++)
    {
      svn_revnum_t from_rev, to_rev;
      svn_fs_root_t *to_root;
      svn_boolean_t use_deltas_for_rev;

      svn_pool_clear(subpool);

      /* Check for cancellation. */
      if (cancel_func)
        SVN_ERR(cancel_func(cancel_baton));

      /* Special-case the initial revision dump: it needs to contain
         *all* nodes, because it's the foundation of all future
         revisions in the dumpfile. */
      if ((i == start_rev) && (! incremental))
        {
          /* Special-special-case a dump of revision 0. */
          if (i == 0)
            {
              /* Just write out the one revision 0 record and move on.
                 The parser might want to use its properties. */
              SVN_ERR(write_revision_record(stream, fs, 0, subpool));
              to_rev = 0;
              goto loop_end;
            }

          /* Compare START_REV to revision 0, so that everything
             appears to be added.  */
          from_rev = 0;
          to_rev = i;
        }
      else
        {
          /* In the normal case, we want to compare consecutive revs. */
          from_rev = i - 1;
          to_rev = i;
        }

      /* Write the revision record. */
      SVN_ERR(write_revision_record(stream, fs, to_rev, subpool));

      /* Fetch the editor which dumps nodes to a file.  Regardless of
         what we've been told, don't use deltas for the first rev of a
         non-incremental dump. */
      use_deltas_for_rev = use_deltas && (incremental || i != start_rev);
      SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton, fs, to_rev,
                              "", stream, notify_func, notify_baton,
                              start_rev, use_deltas_for_rev, FALSE, subpool));

      /* Drive the editor in one way or another. */
      SVN_ERR(svn_fs_revision_root(&to_root, fs, to_rev, subpool));

      /* If this is the first revision of a non-incremental dump,
         we're in for a full tree dump.  Otherwise, we want to simply
         replay the revision.  */
      if ((i == start_rev) && (! incremental))
        {
          svn_fs_root_t *from_root;
          SVN_ERR(svn_fs_revision_root(&from_root, fs, from_rev, subpool));
          SVN_ERR(svn_repos_dir_delta2(from_root, "", "",
                                       to_root, "",
                                       dump_editor, dump_edit_baton,
                                       NULL,
                                       NULL,
                                       FALSE, /* don't send text-deltas */
                                       svn_depth_infinity,
                                       FALSE, /* don't send entry props */
                                       FALSE, /* don't ignore ancestry */
                                       subpool));
        }
      else
        {
          SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE,
                                    dump_editor, dump_edit_baton,
                                    NULL, NULL, subpool));
        }

    loop_end:
      if (notify_func)
        {
          notify->revision = to_rev;
          notify_func(notify_baton, notify, subpool);
        }

      if (dump_edit_baton) /* We never get an edit baton for r0. */
        {
          if (((struct edit_baton *)dump_edit_baton)->found_old_reference)
            found_old_reference = TRUE;
          if (((struct edit_baton *)dump_edit_baton)->found_old_mergeinfo)
            found_old_mergeinfo = TRUE;
        }
    }

  if (notify_func)
    {
      /* Did we issue any warnings about references to revisions older than
         the oldest dumped revision?  If so, then issue a final generic
         warning, since the inline warnings already issued might easily be
         missed. */

      notify = svn_repos_notify_create(svn_repos_notify_dump_end, subpool);
      notify_func(notify_baton, notify, subpool);

      if (found_old_reference)
        {
          notify = svn_repos_notify_create(svn_repos_notify_warning, subpool);

          notify->warning = svn_repos_notify_warning_found_old_reference;
          notify->warning_str = _("The range of revisions dumped "
                                  "contained references to "
                                  "copy sources outside that "
                                  "range.");
          notify_func(notify_baton, notify, subpool);
        }

      /* Ditto if we issued any warnings about old revisions referenced
         in dumped mergeinfo. */
      if (found_old_mergeinfo)
        {
          notify = svn_repos_notify_create(svn_repos_notify_warning, subpool);

          notify->warning = svn_repos_notify_warning_found_old_mergeinfo;
          notify->warning_str = _("The range of revisions dumped "
                                  "contained mergeinfo "
                                  "which reference revisions outside "
                                  "that range.");
          notify_func(notify_baton, notify, subpool);
        }
    }

  svn_pool_destroy(subpool);

  return SVN_NO_ERROR;
}
Beispiel #26
0
/* Inspect the data and/or prop reps of revision REVNUM in FS.  Store
 * reference count tallies in passed hashes (allocated in RESULT_POOL).
 *
 * If PROP_REPS or DATA_REPS is NULL, the respective kind of reps are not
 * tallied.
 *
 * Print progress report to STDERR unless QUIET is true.
 *
 * Use SCRATCH_POOL for temporary allocations.
 */
static svn_error_t *
process_one_revision(svn_fs_t *fs,
                     svn_revnum_t revnum,
                     svn_boolean_t quiet,
                     apr_hash_t *prop_reps,
                     apr_hash_t *data_reps,
                     apr_hash_t *both_reps,
                     apr_pool_t *result_pool,
                     apr_pool_t *scratch_pool)
{
  svn_fs_root_t *rev_root;
  apr_hash_t *paths_changed;
  apr_hash_index_t *hi;

  if (! quiet)
    SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
                                "processing r%ld\n", revnum));

  /* Get the changed paths. */
  SVN_ERR(svn_fs_revision_root(&rev_root, fs, revnum, scratch_pool));
  SVN_ERR(svn_fs_paths_changed2(&paths_changed, rev_root, scratch_pool));

  /* Iterate them. */
  /* ### use iterpool? */
  for (hi = apr_hash_first(scratch_pool, paths_changed);
       hi; hi = apr_hash_next(hi))
    {
      const char *path;
      const svn_fs_path_change2_t *change;
      const svn_fs_id_t *node_rev_id1, *node_rev_id2;
      const svn_fs_id_t *the_id;

      node_revision_t *node_rev;

      path = svn__apr_hash_index_key(hi);
      change = svn__apr_hash_index_val(hi);
      if (! quiet)
        SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
                                    "processing r%ld:%s\n", revnum, path));

      if (change->change_kind == svn_fs_path_change_delete)
        /* Can't ask for reps of PATH at REVNUM if the path no longer exists
         * at that revision! */
        continue;

      /* Okay, we have two node_rev id's for this change: the txn one and
       * the revision one.  We'll use the latter. */
      node_rev_id1 = change->node_rev_id;
      SVN_ERR(svn_fs_node_id(&node_rev_id2, rev_root, path, scratch_pool));

      SVN_ERR_ASSERT(svn_fs_fs__id_txn_id(node_rev_id1) != NULL);
      SVN_ERR_ASSERT(svn_fs_fs__id_rev(node_rev_id2) != SVN_INVALID_REVNUM);

      the_id = node_rev_id2;

      /* Get the node_rev using the chosen node_rev_id. */
      SVN_ERR(svn_fs_fs__get_node_revision(&node_rev, fs, the_id, scratch_pool));

      /* Maybe record the sha1's. */
      SVN_ERR(record(prop_reps, node_rev->prop_rep, result_pool));
      SVN_ERR(record(data_reps, node_rev->data_rep, result_pool));
      SVN_ERR(record(both_reps, node_rev->prop_rep, result_pool));
      SVN_ERR(record(both_reps, node_rev->data_rep, result_pool));
    }

  return SVN_NO_ERROR;
}
Beispiel #27
0
dav_error *
dav_svn__replay_report(const dav_resource *resource,
                       const apr_xml_doc *doc,
                       dav_svn__output *output)
{
  dav_error *derr = NULL;
  svn_revnum_t low_water_mark = SVN_INVALID_REVNUM;
  svn_revnum_t rev;
  const svn_delta_editor_t *editor;
  svn_boolean_t send_deltas = TRUE;
  dav_svn__authz_read_baton arb;
  const char *base_dir;
  apr_bucket_brigade *bb;
  apr_xml_elem *child;
  svn_fs_root_t *root;
  svn_error_t *err;
  void *edit_baton;
  int ns;

  /* In Subversion 1.8, we allowed this REPORT to be issue against a
     revision resource.  Doing so means the REV is part of the request
     URL, and BASE_DIR is embedded in the request body.

     The old-school (and incorrect, see issue #4287 --
     https://issues.apache.org/jira/browse/SVN-4287) way was
     to REPORT on the public URL of the BASE_DIR and embed the REV in
     the report body.
  */
  if (resource->baselined
      && (resource->type == DAV_RESOURCE_TYPE_VERSION))
    {
      rev = resource->info->root.rev;
      base_dir = NULL;
    }
  else
    {
      rev = SVN_INVALID_REVNUM;
      base_dir = resource->info->repos_path;
    }

  arb.r = resource->info->r;
  arb.repos = resource->info->repos;

  ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
  if (ns == -1)
    return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, 0,
                                  "The request does not contain the 'svn:' "
                                  "namespace, so it is not going to have an "
                                  "svn:revision element. That element is "
                                  "required");

  for (child = doc->root->first_child; child != NULL; child = child->next)
    {
      if (child->ns == ns)
        {
          const char *cdata;

          if (strcmp(child->name, "revision") == 0)
            {
              if (SVN_IS_VALID_REVNUM(rev))
                {
                  /* Uh... we already have a revision to use, either
                     because this tag is non-unique or because the
                     request was submitted against a revision-bearing
                     resource URL.  Either way, something's not
                     right.  */
                  return malformed_element_error("revision", resource->pool);
                }

              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              rev = SVN_STR_TO_REV(cdata);
            }
          else if (strcmp(child->name, "low-water-mark") == 0)
            {
              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              if (! cdata)
                return malformed_element_error("low-water-mark",
                                               resource->pool);
              low_water_mark = SVN_STR_TO_REV(cdata);
            }
          else if (strcmp(child->name, "send-deltas") == 0)
            {
              apr_int64_t parsed_val;

              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              if (! cdata)
                return malformed_element_error("send-deltas", resource->pool);
              err = svn_cstring_strtoi64(&parsed_val, cdata, 0, 1, 10);
              if (err)
                {
                  svn_error_clear(err);
                  return malformed_element_error("send-deltas", resource->pool);
                }
              send_deltas = parsed_val != 0;
            }
          else if (strcmp(child->name, "include-path") == 0)
            {
              cdata = dav_xml_get_cdata(child, resource->pool, 1);
              if ((derr = dav_svn__test_canonical(cdata, resource->pool)))
                return derr;

              /* Force BASE_DIR to be a relative path, not an fspath. */
              base_dir = svn_relpath_canonicalize(cdata, resource->pool);
            }
        }
    }

  if (! SVN_IS_VALID_REVNUM(rev))
    return dav_svn__new_error_svn
             (resource->pool, HTTP_BAD_REQUEST, 0, 0,
              "Request was missing the revision argument");

  if (! SVN_IS_VALID_REVNUM(low_water_mark))
    return dav_svn__new_error_svn
             (resource->pool, HTTP_BAD_REQUEST, 0, 0,
              "Request was missing the low-water-mark argument");

  if (! base_dir)
    base_dir = "";

  bb = apr_brigade_create(resource->pool,
                          dav_svn__output_get_bucket_alloc(output));

  if ((err = svn_fs_revision_root(&root, resource->info->repos->fs, rev,
                                  resource->pool)))
    {
      derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                  "Couldn't retrieve revision root",
                                  resource->pool);
      goto cleanup;
    }

  make_editor(&editor, &edit_baton, bb, output,
              dav_svn__get_compression_level(resource->info->r),
              resource->info->svndiff_version,
              resource->pool);

  if ((err = svn_repos_replay2(root, base_dir, low_water_mark,
                               send_deltas, editor, edit_baton,
                               dav_svn__authz_read_func(&arb), &arb,
                               resource->pool)))
    {
      derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                  "Problem replaying revision",
                                  resource->pool);
      goto cleanup;
    }

  if ((err = end_report(edit_baton)))
    {
      derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                  "Problem closing editor drive",
                                  resource->pool);
      goto cleanup;
    }

 cleanup:
  dav_svn__operational_log(resource->info,
                           svn_log__replay(base_dir, rev,
                                           resource->info->r->pool));

  /* Flush the brigade. */
  return dav_svn__final_flush_or_error(resource->info->r, bb, output,
                                       derr, resource->pool);
}
Beispiel #28
0
/* The caller wants to modify REVISION of FSPATH. Is that allowed?  */
static svn_error_t *
can_modify(svn_fs_root_t *txn_root,
           const char *fspath,
           svn_revnum_t revision,
           apr_pool_t *scratch_pool)
{
    svn_revnum_t created_rev;

    /* Out-of-dateness check:  compare the created-rev of the node
       in the txn against the created-rev of FSPATH.  */
    SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, fspath,
                                    scratch_pool));

    /* Uncommitted nodes (eg. a descendent of a copy/move/rotate destination)
       have no (committed) revision number. Let the caller go ahead and
       modify these nodes.

       Note: strictly speaking, they might be performing an "illegal" edit
       in certain cases, but let's just assume they're Good Little Boys.

       If CREATED_REV is invalid, that means it's already mutable in the
       txn, which means it has already passed this out-of-dateness check.
       (Usually, this happens when looking at a parent directory of an
       already-modified node)  */
    if (!SVN_IS_VALID_REVNUM(created_rev))
        return SVN_NO_ERROR;

    /* If the node is immutable (has a revision), then the caller should
       have supplied a valid revision number [that they expect to change].
       The checks further below will determine the out-of-dateness of the
       specified revision.  */
    /* ### ugh. descendents of copy/move/rotate destinations carry along
       ### their original immutable state and (thus) a valid CREATED_REV.
       ### but they are logically uncommitted, so the caller will pass
       ### SVN_INVALID_REVNUM. (technically, the caller could provide
       ### ORIGINAL_REV, but that is semantically incorrect for the Ev2
       ### API).
       ###
       ### for now, we will assume the caller knows what they are doing
       ### and an invalid revision implies such a descendent. in the
       ### future, we could examine the ancestor chain looking for a
       ### copy/move/rotate-here node and allow the modification (and the
       ### converse: if no such ancestor, the caller must specify the
       ### correct/intended revision to modify).
    */
#if 1
    if (!SVN_IS_VALID_REVNUM(revision))
        return SVN_NO_ERROR;
#else
    if (!SVN_IS_VALID_REVNUM(revision))
        /* ### use a custom error code?  */
        return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
                                 _("Revision for modifying '%s' is required"),
                                 fspath);
#endif

    if (revision < created_rev)
    {
        /* We asked to change a node that is *older* than what we found
           in the transaction. The client is out of date.  */
        return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL,
                                 _("'%s' is out of date; try updating"),
                                 fspath);
    }

    if (revision > created_rev)
    {
        /* We asked to change a node that is *newer* than what we found
           in the transaction. Given that the transaction was based off
           of 'youngest', then either:
           - the caller asked to modify a future node
           - the caller has committed more revisions since this txn
           was constructed, and is asking to modify a node in one
           of those new revisions.
           In either case, the node may not have changed in those new
           revisions; use the node's ID to determine this case.  */
        const svn_fs_id_t *txn_noderev_id;
        svn_fs_root_t *rev_root;
        const svn_fs_id_t *new_noderev_id;

        /* The ID of the node that we would be modifying in the txn  */
        SVN_ERR(svn_fs_node_id(&txn_noderev_id, txn_root, fspath,
                               scratch_pool));

        /* Get the ID from the future/new revision.  */
        SVN_ERR(svn_fs_revision_root(&rev_root, svn_fs_root_fs(txn_root),
                                     revision, scratch_pool));
        SVN_ERR(svn_fs_node_id(&new_noderev_id, rev_root, fspath,
                               scratch_pool));
        svn_fs_close_root(rev_root);

        /* Has the target node changed in the future?  */
        if (svn_fs_compare_ids(txn_noderev_id, new_noderev_id) != 0)
        {
            /* Restarting the commit will base the txn on the future/new
               revision, allowing the modification at REVISION.  */
            /* ### use a custom error code  */
            return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
                                     _("'%s' has been modified since the "
                                       "commit began (restart the commit)"),
                                     fspath);
        }
    }

    return SVN_NO_ERROR;
}
Beispiel #29
0
dav_error *
dav_svn__get_inherited_props_report(const dav_resource *resource,
                                    const apr_xml_doc *doc,
                                    ap_filter_t *output)
{
  svn_error_t *serr;
  dav_error *derr = NULL;
  apr_xml_elem *child;
  apr_array_header_t *inherited_props;
  dav_svn__authz_read_baton arb;
  int ns;
  apr_bucket_brigade *bb;
  const char *path = "/";
  svn_fs_root_t *root;
  int i;
  svn_revnum_t rev = SVN_INVALID_REVNUM;
  apr_pool_t *iterpool;

  /* Sanity check. */
  if (!resource->info->repos_path)
    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0,
                              "The request does not specify a repository path");
  ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
  if (ns == -1)
    {
      return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0,
                                    "The request does not contain the 'svn:' "
                                    "namespace, so it is not going to have "
                                    "certain required elements");
    }

  iterpool = svn_pool_create(resource->pool);

  for (child = doc->root->first_child;
       child != NULL;
       child = child->next)
    {
      /* if this element isn't one of ours, then skip it */
      if (child->ns != ns)
        continue;

      if (strcmp(child->name, SVN_DAV__REVISION) == 0)
        {
          rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, iterpool, 1));
        }
      else if (strcmp(child->name, SVN_DAV__PATH) == 0)
        {
          path = dav_xml_get_cdata(child, resource->pool, 0);
          if ((derr = dav_svn__test_canonical(path, iterpool)))
            return derr;
          path = svn_fspath__join(resource->info->repos_path, path,
                                  resource->pool);
        }
      /* else unknown element; skip it */
    }

  /* Build authz read baton */
  arb.r = resource->info->r;
  arb.repos = resource->info->repos;

  /* Build inherited property brigade */
  bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);

  serr = svn_fs_revision_root(&root, resource->info->repos->fs,
                              rev, resource->pool);
  if (serr != NULL)
    return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                "couldn't retrieve revision root",
                                resource->pool);

  serr = svn_repos_fs_get_inherited_props(&inherited_props, root, path, NULL,
                                          dav_svn__authz_read_func(&arb),
                                          &arb, resource->pool, iterpool);
  if (serr)
    {
      derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL,
                                  resource->pool);
      goto cleanup;
    }

  serr = dav_svn__brigade_puts(bb, output,
                               DAV_XML_HEADER DEBUG_CR
                               "<S:" SVN_DAV__INHERITED_PROPS_REPORT " "
                               "xmlns:S=\"" SVN_XML_NAMESPACE "\" "
                               "xmlns:D=\"DAV:\">" DEBUG_CR);
  if (serr)
    {
      derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL,
                                  resource->pool);
      goto cleanup;
    }

  for (i = 0; i < inherited_props->nelts; i++)
    {
      svn_prop_inherited_item_t *elt =
        APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);

      svn_pool_clear(iterpool);

      serr = dav_svn__brigade_printf(
        bb, output,
        "<S:" SVN_DAV__IPROP_ITEM ">"
        DEBUG_CR
        "<S:" SVN_DAV__IPROP_PATH ">%s</S:" SVN_DAV__IPROP_PATH ">"
        DEBUG_CR,
        apr_xml_quote_string(resource->pool, elt->path_or_url, 0));

      if (!serr)
        {
          apr_hash_index_t *hi;

          for (hi = apr_hash_first(resource->pool, elt->prop_hash);
               hi;
               hi = apr_hash_next(hi))
            {
              const char *propname = apr_hash_this_key(hi);
              svn_string_t *propval = apr_hash_this_val(hi);
              const char *xml_safe;

              serr = dav_svn__brigade_printf(
                bb, output,
                "<S:" SVN_DAV__IPROP_PROPNAME ">%s</S:"
                SVN_DAV__IPROP_PROPNAME ">" DEBUG_CR,
                apr_xml_quote_string(iterpool, propname, 0));

              if (!serr)
                {
                  if (svn_xml_is_xml_safe(propval->data, propval->len))
                    {
                      svn_stringbuf_t *tmp = NULL;
                      svn_xml_escape_cdata_string(&tmp, propval,
                                                  iterpool);
                      xml_safe = tmp->data;
                      serr = dav_svn__brigade_printf(
                        bb, output,
                        "<S:" SVN_DAV__IPROP_PROPVAL ">%s</S:"
                        SVN_DAV__IPROP_PROPVAL ">" DEBUG_CR, xml_safe);
                    }
                  else
                    {
                      xml_safe = svn_base64_encode_string2(
                        propval, TRUE, iterpool)->data;
                      serr = dav_svn__brigade_printf(
                        bb, output,
                        "<S:" SVN_DAV__IPROP_PROPVAL
                        " encoding=\"base64\"" ">%s</S:"
                        SVN_DAV__IPROP_PROPVAL ">" DEBUG_CR, xml_safe);
                    }
                }

              if (serr)
                break;
            }
          if (!serr)
            serr = dav_svn__brigade_printf(bb, output,
                                           "</S:" SVN_DAV__IPROP_ITEM ">"
                                           DEBUG_CR);
        }

      if (serr)
        {
          derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                      "Error ending REPORT response.",
                                      resource->pool);
          goto cleanup;
        }
    }

  if ((serr = dav_svn__brigade_puts(bb, output,
                                    "</S:" SVN_DAV__INHERITED_PROPS_REPORT ">"
                                    DEBUG_CR)))
    {
      derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "Error ending REPORT response.",
                                  resource->pool);
      goto cleanup;
    }

 cleanup:

  /* Log this 'high level' svn action. */
  dav_svn__operational_log(resource->info,
                           svn_log__get_inherited_props(path, rev,
                                                        resource->pool));
  svn_pool_destroy(iterpool);
  return dav_svn__final_flush_or_error(resource->info->r, bb, output,
                                       derr, resource->pool);
}
Beispiel #30
0
/* This helper is the main "meat" of the editor -- it does all the
   work of writing a node record.

   Write out a node record for PATH of type KIND under EB->FS_ROOT.
   ACTION describes what is happening to the node (see enum svn_node_action).
   Write record to writable EB->STREAM, using EB->BUFFER to write in chunks.

   If the node was itself copied, IS_COPY is TRUE and the
   path/revision of the copy source are in CMP_PATH/CMP_REV.  If
   IS_COPY is FALSE, yet CMP_PATH/CMP_REV are valid, this node is part
   of a copied subtree.
  */
static svn_error_t *
dump_node(struct edit_baton *eb,
          const char *path,
          svn_node_kind_t kind,
          enum svn_node_action action,
          svn_boolean_t is_copy,
          const char *cmp_path,
          svn_revnum_t cmp_rev,
          apr_pool_t *pool)
{
  svn_stringbuf_t *propstring;
  svn_filesize_t content_length = 0;
  apr_size_t len;
  svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE;
  const char *compare_path = path;
  svn_revnum_t compare_rev = eb->current_rev - 1;
  svn_fs_root_t *compare_root = NULL;
  apr_file_t *delta_file = NULL;

  /* Maybe validate the path. */
  if (eb->verify || eb->notify_func)
    {
      svn_error_t *err = svn_fs__path_valid(path, pool);

      if (err)
        {
          if (eb->notify_func)
            {
              char errbuf[512]; /* ### svn_strerror() magic number  */
              svn_repos_notify_t *notify;
              notify = svn_repos_notify_create(svn_repos_notify_warning, pool);

              notify->warning = svn_repos_notify_warning_invalid_fspath;
              notify->warning_str = apr_psprintf(
                     pool,
                     _("E%06d: While validating fspath '%s': %s"),
                     err->apr_err, path,
                     svn_err_best_message(err, errbuf, sizeof(errbuf)));

              eb->notify_func(eb->notify_baton, notify, pool);
            }

          /* Return the error in addition to notifying about it. */
          if (eb->verify)
            return svn_error_trace(err);
          else
            svn_error_clear(err);
        }
    }

  /* Write out metadata headers for this file node. */
  SVN_ERR(svn_stream_printf(eb->stream, pool,
                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
                            path));
  if (kind == svn_node_file)
    SVN_ERR(svn_stream_printf(eb->stream, pool,
                              SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
  else if (kind == svn_node_dir)
    SVN_ERR(svn_stream_printf(eb->stream, pool,
                              SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));

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

  /* Validate the comparison path/rev. */
  if (ARE_VALID_COPY_ARGS(cmp_path, cmp_rev))
    {
      compare_path = cmp_path;
      compare_rev = cmp_rev;
    }

  if (action == svn_node_action_change)
    {
      SVN_ERR(svn_stream_printf(eb->stream, pool,
                                SVN_REPOS_DUMPFILE_NODE_ACTION
                                ": change\n"));

      /* either the text or props changed, or possibly both. */
      SVN_ERR(svn_fs_revision_root(&compare_root,
                                   svn_fs_root_fs(eb->fs_root),
                                   compare_rev, pool));

      SVN_ERR(svn_fs_props_changed(&must_dump_props,
                                   compare_root, compare_path,
                                   eb->fs_root, path, pool));
      if (kind == svn_node_file)
        SVN_ERR(svn_fs_contents_changed(&must_dump_text,
                                        compare_root, compare_path,
                                        eb->fs_root, path, pool));
    }
  else if (action == svn_node_action_replace)
    {
      if (! is_copy)
        {
          /* a simple delete+add, implied by a single 'replace' action. */
          SVN_ERR(svn_stream_printf(eb->stream, pool,
                                    SVN_REPOS_DUMPFILE_NODE_ACTION
                                    ": replace\n"));

          /* definitely need to dump all content for a replace. */
          if (kind == svn_node_file)
            must_dump_text = TRUE;
          must_dump_props = TRUE;
        }
      else
        {
          /* more complex:  delete original, then add-with-history.  */

          /* the path & kind headers have already been printed;  just
             add a delete action, and end the current record.*/
          SVN_ERR(svn_stream_printf(eb->stream, pool,
                                    SVN_REPOS_DUMPFILE_NODE_ACTION
                                    ": delete\n\n"));

          /* recurse:  print an additional add-with-history record. */
          SVN_ERR(dump_node(eb, path, kind, svn_node_action_add,
                            is_copy, compare_path, compare_rev, pool));

          /* we can leave this routine quietly now, don't need to dump
             any content;  that was already done in the second record. */
          must_dump_text = FALSE;
          must_dump_props = FALSE;
        }
    }
  else if (action == svn_node_action_delete)
    {
      SVN_ERR(svn_stream_printf(eb->stream, pool,
                                SVN_REPOS_DUMPFILE_NODE_ACTION
                                ": delete\n"));

      /* we can leave this routine quietly now, don't need to dump
         any content. */
      must_dump_text = FALSE;
      must_dump_props = FALSE;
    }
  else if (action == svn_node_action_add)
    {
      SVN_ERR(svn_stream_printf(eb->stream, pool,
                                SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));

      if (! is_copy)
        {
          /* Dump all contents for a simple 'add'. */
          if (kind == svn_node_file)
            must_dump_text = TRUE;
          must_dump_props = TRUE;
        }
      else
        {
          if (!eb->verify && cmp_rev < eb->oldest_dumped_rev
              && eb->notify_func)
            {
              svn_repos_notify_t *notify =
                    svn_repos_notify_create(svn_repos_notify_warning, pool);

              notify->warning = svn_repos_notify_warning_found_old_reference;
              notify->warning_str = apr_psprintf(
                     pool,
                     _("Referencing data in revision %ld,"
                       " which is older than the oldest"
                       " dumped revision (r%ld).  Loading this dump"
                       " into an empty repository"
                       " will fail."),
                     cmp_rev, eb->oldest_dumped_rev);
              eb->found_old_reference = TRUE;
              eb->notify_func(eb->notify_baton, notify, pool);
            }

          SVN_ERR(svn_stream_printf(eb->stream, pool,
                                    SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
                                    ": %ld\n"
                                    SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
                                    ": %s\n",
                                    cmp_rev, cmp_path));

          SVN_ERR(svn_fs_revision_root(&compare_root,
                                       svn_fs_root_fs(eb->fs_root),
                                       compare_rev, pool));

          /* Need to decide if the copied node had any extra textual or
             property mods as well.  */
          SVN_ERR(svn_fs_props_changed(&must_dump_props,
                                       compare_root, compare_path,
                                       eb->fs_root, path, pool));
          if (kind == svn_node_file)
            {
              svn_checksum_t *checksum;
              const char *hex_digest;
              SVN_ERR(svn_fs_contents_changed(&must_dump_text,
                                              compare_root, compare_path,
                                              eb->fs_root, path, pool));

              SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5,
                                           compare_root, compare_path,
                                           FALSE, pool));
              hex_digest = svn_checksum_to_cstring(checksum, pool);
              if (hex_digest)
                SVN_ERR(svn_stream_printf(eb->stream, pool,
                                      SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5
                                      ": %s\n", hex_digest));

              SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1,
                                           compare_root, compare_path,
                                           FALSE, pool));
              hex_digest = svn_checksum_to_cstring(checksum, pool);
              if (hex_digest)
                SVN_ERR(svn_stream_printf(eb->stream, pool,
                                      SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_SHA1
                                      ": %s\n", hex_digest));
            }
        }
    }

  if ((! must_dump_text) && (! must_dump_props))
    {
      /* If we're not supposed to dump text or props, so be it, we can
         just go home.  However, if either one needs to be dumped,
         then our dumpstream format demands that at a *minimum*, we
         see a lone "PROPS-END" as a divider between text and props
         content within the content-block. */
      len = 2;
      return svn_stream_write(eb->stream, "\n\n", &len); /* ### needed? */
    }

  /*** Start prepping content to dump... ***/

  /* If we are supposed to dump properties, write out a property
     length header and generate a stringbuf that contains those
     property values here. */
  if (must_dump_props)
    {
      apr_hash_t *prophash, *oldhash = NULL;
      apr_size_t proplen;
      svn_stream_t *propstream;

      SVN_ERR(svn_fs_node_proplist(&prophash, eb->fs_root, path, pool));

      /* If this is a partial dump, then issue a warning if we dump mergeinfo
         properties that refer to revisions older than the first revision
         dumped. */
      if (!eb->verify && eb->notify_func && eb->oldest_dumped_rev > 1)
        {
          svn_string_t *mergeinfo_str = apr_hash_get(prophash,
                                                     SVN_PROP_MERGEINFO,
                                                     APR_HASH_KEY_STRING);
          if (mergeinfo_str)
            {
              svn_mergeinfo_t mergeinfo, old_mergeinfo;

              SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_str->data,
                                          pool));
              SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
                &old_mergeinfo, mergeinfo,
                eb->oldest_dumped_rev - 1, 0,
                TRUE, pool, pool));
              if (apr_hash_count(old_mergeinfo))
                {
                  svn_repos_notify_t *notify =
                    svn_repos_notify_create(svn_repos_notify_warning, pool);

                  notify->warning = svn_repos_notify_warning_found_old_mergeinfo;
                  notify->warning_str = apr_psprintf(
                    pool,
                    _("Mergeinfo referencing revision(s) prior "
                      "to the oldest dumped revision (r%ld). "
                      "Loading this dump may result in invalid "
                      "mergeinfo."),
                    eb->oldest_dumped_rev);

                  eb->found_old_mergeinfo = TRUE;
                  eb->notify_func(eb->notify_baton, notify, pool);
                }
            }
        }

      if (eb->use_deltas && compare_root)
        {
          /* Fetch the old property hash to diff against and output a header
             saying that our property contents are a delta. */
          SVN_ERR(svn_fs_node_proplist(&oldhash, compare_root, compare_path,
                                       pool));
          SVN_ERR(svn_stream_printf(eb->stream, pool,
                                    SVN_REPOS_DUMPFILE_PROP_DELTA
                                    ": true\n"));
        }
      else
        oldhash = apr_hash_make(pool);
      propstring = svn_stringbuf_create_ensure(0, pool);
      propstream = svn_stream_from_stringbuf(propstring, pool);
      SVN_ERR(svn_hash_write_incremental(prophash, oldhash, propstream,
                                         "PROPS-END", pool));
      SVN_ERR(svn_stream_close(propstream));
      proplen = propstring->len;
      content_length += proplen;
      SVN_ERR(svn_stream_printf(eb->stream, pool,
                                SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
                                ": %" APR_SIZE_T_FMT "\n", proplen));
    }

  /* If we are supposed to dump text, write out a text length header
     here, and an MD5 checksum (if available). */
  if (must_dump_text && (kind == svn_node_file))
    {
      svn_checksum_t *checksum;
      const char *hex_digest;
      svn_filesize_t textlen;

      if (eb->use_deltas)
        {
          /* Compute the text delta now and write it into a temporary
             file, so that we can find its length.  Output a header
             saying our text contents are a delta. */
          SVN_ERR(store_delta(&delta_file, &textlen, compare_root,
                              compare_path, eb->fs_root, path, pool));
          SVN_ERR(svn_stream_printf(eb->stream, pool,
                                    SVN_REPOS_DUMPFILE_TEXT_DELTA
                                    ": true\n"));

          if (compare_root)
            {
              SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5,
                                           compare_root, compare_path,
                                           FALSE, pool));
              hex_digest = svn_checksum_to_cstring(checksum, pool);
              if (hex_digest)
                SVN_ERR(svn_stream_printf(eb->stream, pool,
                                          SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5
                                          ": %s\n", hex_digest));

              SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1,
                                           compare_root, compare_path,
                                           FALSE, pool));
              hex_digest = svn_checksum_to_cstring(checksum, pool);
              if (hex_digest)
                SVN_ERR(svn_stream_printf(eb->stream, pool,
                                      SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_SHA1
                                      ": %s\n", hex_digest));
            }
        }
      else
        {
          /* Just fetch the length of the file. */
          SVN_ERR(svn_fs_file_length(&textlen, eb->fs_root, path, pool));
        }

      content_length += textlen;
      SVN_ERR(svn_stream_printf(eb->stream, pool,
                                SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH
                                ": %" SVN_FILESIZE_T_FMT "\n", textlen));

      SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5,
                                   eb->fs_root, path, FALSE, pool));
      hex_digest = svn_checksum_to_cstring(checksum, pool);
      if (hex_digest)
        SVN_ERR(svn_stream_printf(eb->stream, pool,
                                  SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5
                                  ": %s\n", hex_digest));

      SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1,
                                   eb->fs_root, path, FALSE, pool));
      hex_digest = svn_checksum_to_cstring(checksum, pool);
      if (hex_digest)
        SVN_ERR(svn_stream_printf(eb->stream, pool,
                                  SVN_REPOS_DUMPFILE_TEXT_CONTENT_SHA1
                                  ": %s\n", hex_digest));
    }

  /* 'Content-length:' is the last header before we dump the content,
     and is the sum of the text and prop contents lengths.  We write
     this only for the benefit of non-Subversion RFC-822 parsers. */
  SVN_ERR(svn_stream_printf(eb->stream, pool,
                            SVN_REPOS_DUMPFILE_CONTENT_LENGTH
                            ": %" SVN_FILESIZE_T_FMT "\n\n",
                            content_length));

  /* Dump property content if we're supposed to do so. */
  if (must_dump_props)
    {
      len = propstring->len;
      SVN_ERR(svn_stream_write(eb->stream, propstring->data, &len));
    }

  /* Dump text content */
  if (must_dump_text && (kind == svn_node_file))
    {
      svn_stream_t *contents;

      if (delta_file)
        {
          /* Make sure to close the underlying file when the stream is
             closed. */
          contents = svn_stream_from_aprfile2(delta_file, FALSE, pool);
        }
      else
        SVN_ERR(svn_fs_file_contents(&contents, eb->fs_root, path, pool));

      SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(eb->stream, pool),
                               NULL, NULL, pool));
    }

  len = 2;
  return svn_stream_write(eb->stream, "\n\n", &len); /* ### needed? */
}