Пример #1
0
svn_error_t *
svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p,
                                   svn_repos_t *repos,
                                   svn_revnum_t rev,
                                   apr_hash_t *revprop_table,
                                   apr_pool_t *pool)
{
    apr_array_header_t *revprops;
    const char *txn_name;
    svn_string_t *author = svn_hash_gets(revprop_table, SVN_PROP_REVISION_AUTHOR);
    apr_hash_t *hooks_env;

    /* Parse the hooks-env file (if any). */
    SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
                                       pool, pool));

    /* Begin the transaction, ask for the fs to do on-the-fly lock checks.
       We fetch its name, too, so the start-commit hook can use it.  */
    SVN_ERR(svn_fs_begin_txn2(txn_p, repos->fs, rev,
                              SVN_FS_TXN_CHECK_LOCKS, pool));
    SVN_ERR(svn_fs_txn_name(&txn_name, *txn_p, pool));

    /* We pass the revision properties to the filesystem by adding them
       as properties on the txn.  Later, when we commit the txn, these
       properties will be copied into the newly created revision. */
    revprops = svn_prop_hash_to_array(revprop_table, pool);
    SVN_ERR(svn_repos_fs_change_txn_props(*txn_p, revprops, pool));

    /* Run start-commit hooks. */
    SVN_ERR(svn_repos__hooks_start_commit(repos, hooks_env,
                                          author ? author->data : NULL,
                                          repos->client_capabilities, txn_name,
                                          pool));
    return SVN_NO_ERROR;
}
Пример #2
0
/* This implements svn_editor_cb_complete_t */
static svn_error_t *
complete_cb(void *baton,
            apr_pool_t *scratch_pool)
{
  struct ev2_baton *eb = baton;
  svn_revnum_t revision;
  svn_error_t *post_commit_err;
  const char *conflict_path;
  svn_error_t *err;
  const char *post_commit_errstr;
  apr_hash_t *hooks_env;

  /* Parse the hooks-env file (if any). */
  SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, eb->repos->hooks_env_path,
                                     scratch_pool, scratch_pool));

  /* The transaction has been fully edited. Let the pre-commit hook
     have a look at the thing.  */
  SVN_ERR(svn_repos__hooks_pre_commit(eb->repos, hooks_env,
                                      eb->txn_name, scratch_pool));

  /* Hook is done. Let's do the actual commit.  */
  SVN_ERR(svn_fs__editor_commit(&revision, &post_commit_err, &conflict_path,
                                eb->inner, scratch_pool, scratch_pool));

  /* Did a conflict occur during the commit process?  */
  if (conflict_path != NULL)
    return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL,
                             _("Conflict at '%s'"), conflict_path);

  /* Since did not receive an error during the commit process, and no
     conflict was specified... we committed a revision. Run the hooks.
     Other errors may have occurred within the FS (specified by the
     POST_COMMIT_ERR localvar), but we need to run the hooks.  */
  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
  err = svn_repos__hooks_post_commit(eb->repos, hooks_env, revision,
                                     eb->txn_name, scratch_pool);
  if (err)
    err = svn_error_create(SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err,
                           _("Commit succeeded, but post-commit hook failed"));

  /* Combine the FS errors with the hook errors, and stringify.  */
  err = svn_error_compose_create(post_commit_err, err);
  if (err)
    {
      post_commit_errstr = svn_repos__post_commit_error_str(err, scratch_pool);
      svn_error_clear(err);
    }
  else
    {
      post_commit_errstr = NULL;
    }

  return svn_error_trace(invoke_commit_cb(eb->commit_cb, eb->commit_baton,
                                          eb->repos->fs, revision,
                                          post_commit_errstr,
                                          scratch_pool));
}
Пример #3
0
svn_error_t *
svn_repos_fs_lock(svn_lock_t **lock,
                  svn_repos_t *repos,
                  const char *path,
                  const char *token,
                  const char *comment,
                  svn_boolean_t is_dav_comment,
                  apr_time_t expiration_date,
                  svn_revnum_t current_rev,
                  svn_boolean_t steal_lock,
                  apr_pool_t *pool)
{
    svn_error_t *err;
    svn_fs_access_t *access_ctx = NULL;
    const char *username = NULL;
    const char *new_token;
    apr_array_header_t *paths;
    apr_hash_t *hooks_env;

    /* Parse the hooks-env file (if any). */
    SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
                                       pool, pool));

    /* Setup an array of paths in anticipation of the ra layers handling
       multiple locks in one request (1.3 most likely).  This is only
       used by svn_repos__hooks_post_lock. */
    paths = apr_array_make(pool, 1, sizeof(const char *));
    APR_ARRAY_PUSH(paths, const char *) = path;

    SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs));
    if (access_ctx)
        SVN_ERR(svn_fs_access_get_username(&username, access_ctx));

    if (! username)
        return svn_error_createf
               (SVN_ERR_FS_NO_USER, NULL,
                "Cannot lock path '%s', no authenticated username available.", path);

    /* Run pre-lock hook.  This could throw error, preventing
       svn_fs_lock() from happening. */
    SVN_ERR(svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path,
                                      username, comment, steal_lock, pool));
    if (*new_token)
        token = new_token;

    /* Lock. */
    SVN_ERR(svn_fs_lock(lock, repos->fs, path, token, comment, is_dav_comment,
                        expiration_date, current_rev, steal_lock, pool));

    /* Run post-lock hook. */
    if ((err = svn_repos__hooks_post_lock(repos, hooks_env,
                                          paths, username, pool)))
        return svn_error_create
               (SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, err,
                "Lock succeeded, but post-lock hook failed");

    return SVN_NO_ERROR;
}
Пример #4
0
svn_error_t *
svn_repos_fs_commit_txn(const char **conflict_p,
                        svn_repos_t *repos,
                        svn_revnum_t *new_rev,
                        svn_fs_txn_t *txn,
                        apr_pool_t *pool)
{
    svn_error_t *err, *err2;
    const char *txn_name;
    apr_hash_t *props;
    apr_pool_t *iterpool;
    apr_hash_index_t *hi;
    apr_hash_t *hooks_env;

    *new_rev = SVN_INVALID_REVNUM;

    /* Parse the hooks-env file (if any). */
    SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
                                       pool, pool));

    /* Run pre-commit hooks. */
    SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
    SVN_ERR(svn_repos__hooks_pre_commit(repos, hooks_env, txn_name, pool));

    /* Remove any ephemeral transaction properties. */
    SVN_ERR(svn_fs_txn_proplist(&props, txn, pool));
    iterpool = svn_pool_create(pool);
    for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
    {
        const void *key;
        apr_hash_this(hi, &key, NULL, NULL);

        svn_pool_clear(iterpool);

        if (strncmp(key, SVN_PROP_TXN_PREFIX,
                    (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0)
        {
            SVN_ERR(svn_fs_change_txn_prop(txn, key, NULL, iterpool));
        }
    }
    svn_pool_destroy(iterpool);

    /* Commit. */
    err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool);
    if (! SVN_IS_VALID_REVNUM(*new_rev))
        return err;

    /* Run post-commit hooks. */
    if ((err2 = svn_repos__hooks_post_commit(repos, hooks_env,
                *new_rev, txn_name, pool)))
    {
        err2 = svn_error_create
               (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err2,
                _("Commit succeeded, but post-commit hook failed"));
    }

    return svn_error_compose_create(err, err2);
}
Пример #5
0
svn_error_t *
svn_repos_fs_unlock(svn_repos_t *repos,
                    const char *path,
                    const char *token,
                    svn_boolean_t break_lock,
                    apr_pool_t *pool)
{
    svn_error_t *err;
    svn_fs_access_t *access_ctx = NULL;
    const char *username = NULL;
    apr_array_header_t *paths;
    apr_hash_t *hooks_env;

    /* Parse the hooks-env file (if any). */
    SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
                                       pool, pool));

    /* Setup an array of paths in anticipation of the ra layers handling
       multiple locks in one request (1.3 most likely).  This is only
       used by svn_repos__hooks_post_lock. */
    paths = apr_array_make(pool, 1, sizeof(const char *));
    APR_ARRAY_PUSH(paths, const char *) = path;

    SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs));
    if (access_ctx)
        SVN_ERR(svn_fs_access_get_username(&username, access_ctx));

    if (! break_lock && ! username)
        return svn_error_createf
               (SVN_ERR_FS_NO_USER, NULL,
                _("Cannot unlock path '%s', no authenticated username available"),
                path);

    /* Run pre-unlock hook.  This could throw error, preventing
       svn_fs_unlock() from happening. */
    SVN_ERR(svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token,
                                        break_lock, pool));

    /* Unlock. */
    SVN_ERR(svn_fs_unlock(repos->fs, path, token, break_lock, pool));

    /* Run post-unlock hook. */
    if ((err = svn_repos__hooks_post_unlock(repos, hooks_env, paths,
                                            username, pool)))
        return svn_error_create
               (SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, err,
                _("Unlock succeeded, but post-unlock hook failed"));

    return SVN_NO_ERROR;
}
Пример #6
0
static svn_error_t *
close_revision(void *baton)
{
  struct revision_baton *rb = baton;
  struct parse_baton *pb = rb->pb;
  const char *conflict_msg = NULL;
  svn_revnum_t committed_rev;
  svn_error_t *err;
  const char *txn_name = NULL;
  apr_hash_t *hooks_env;

  /* If we're skipping this revision we're done here. */
  if (rb->skipped)
    return SVN_NO_ERROR;

  if (rb->rev == 0)
    {
      /* Special case: set revision 0 properties when loading into an
         'empty' filesystem. */
      svn_revnum_t youngest_rev;

      SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, rb->pool));

      if (youngest_rev == 0)
        {
          apr_hash_t *orig_props;
          apr_hash_t *new_props;
          apr_array_header_t *diff;
          int i;

          SVN_ERR(svn_fs_revision_proplist(&orig_props, pb->fs, 0, rb->pool));
          new_props = svn_prop_array_to_hash(rb->revprops, rb->pool);
          SVN_ERR(svn_prop_diffs(&diff, new_props, orig_props, rb->pool));

          for (i = 0; i < diff->nelts; i++)
          {
              const svn_prop_t *prop = &APR_ARRAY_IDX(diff, i, svn_prop_t);

              SVN_ERR(change_rev_prop(pb->repos, 0, prop->name, prop->value,
                                      pb->validate_props, rb->pool));
          }
        }

      return SVN_NO_ERROR;
    }

  /* If the dumpstream doesn't have an 'svn:date' property and we
     aren't ignoring the dates in the dumpstream altogether, remove
     any 'svn:date' revision property that was set by FS layer when
     the TXN was created.  */
  if (! (pb->ignore_dates || rb->datestamp))
    {
      svn_prop_t *prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t);
      prop->name = SVN_PROP_REVISION_DATE;
      prop->value = NULL;
    }

  /* Apply revision property changes. */
  if (rb->pb->validate_props)
    SVN_ERR(svn_repos_fs_change_txn_props(rb->txn, rb->revprops, rb->pool));
  else
    SVN_ERR(svn_fs_change_txn_props(rb->txn, rb->revprops, rb->pool));

  /* Get the txn name and hooks environment if they will be needed. */
  if (pb->use_pre_commit_hook || pb->use_post_commit_hook)
    {
      SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, pb->repos->hooks_env_path,
                                         rb->pool, rb->pool));

      err = svn_fs_txn_name(&txn_name, rb->txn, rb->pool);
      if (err)
        {
          svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
          return svn_error_trace(err);
        }
    }

  /* Run the pre-commit hook, if so commanded. */
  if (pb->use_pre_commit_hook)
    {
      err = svn_repos__hooks_pre_commit(pb->repos, hooks_env,
                                        txn_name, rb->pool);
      if (err)
        {
          svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
          return svn_error_trace(err);
        }
    }

  /* Commit. */
  err = svn_fs_commit_txn(&conflict_msg, &committed_rev, rb->txn, rb->pool);
  if (SVN_IS_VALID_REVNUM(committed_rev))
    {
      if (err)
        {
          /* ### Log any error, but better yet is to rev
             ### close_revision()'s API to allow both committed_rev and err
             ### to be returned, see #3768. */
          svn_error_clear(err);
        }
    }
  else
    {
      svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
      if (conflict_msg)
        return svn_error_quick_wrap(err, conflict_msg);
      else
        return svn_error_trace(err);
    }

  /* Run post-commit hook, if so commanded.  */
  if (pb->use_post_commit_hook)
    {
      if ((err = svn_repos__hooks_post_commit(pb->repos, hooks_env,
                                              committed_rev, txn_name,
                                              rb->pool)))
        return svn_error_create
          (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err,
           _("Commit succeeded, but post-commit hook failed"));
    }

  /* After a successful commit, must record the dump-rev -> in-repos-rev
     mapping, so that copyfrom instructions in the dump file can look up the
     correct repository revision to copy from. */
  set_revision_mapping(pb->rev_map, rb->rev, committed_rev);

  /* If the incoming dump stream has non-contiguous revisions (e.g. from
     using svndumpfilter --drop-empty-revs without --renumber-revs) then
     we must account for the missing gaps in PB->REV_MAP.  Otherwise we
     might not be able to map all mergeinfo source revisions to the correct
     revisions in the target repos. */
  if ((pb->last_rev_mapped != SVN_INVALID_REVNUM)
      && (rb->rev != pb->last_rev_mapped + 1))
    {
      svn_revnum_t i;

      for (i = pb->last_rev_mapped + 1; i < rb->rev; i++)
        {
          set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped);
        }
    }

  /* Update our "last revision mapped". */
  pb->last_rev_mapped = rb->rev;

  /* Deltify the predecessors of paths changed in this revision. */
  SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->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_txn_committed,
                                        pb->notify_pool);

      notify->new_revision = committed_rev;
      notify->old_revision = ((committed_rev == rb->rev)
                                    ? SVN_INVALID_REVNUM
                                    : rb->rev);
      pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
      svn_pool_clear(pb->notify_pool);
    }

  return SVN_NO_ERROR;
}
Пример #7
0
static svn_error_t *
close_revision(void *baton)
{
  struct revision_baton *rb = baton;
  struct parse_baton *pb = rb->pb;
  const char *conflict_msg = NULL;
  svn_revnum_t committed_rev;
  svn_error_t *err;
  const char *txn_name = NULL;
  apr_hash_t *hooks_env;

  /* If we're skipping this revision or it has an invalid revision
     number, we're done here. */
  if (rb->skipped || (rb->rev <= 0))
    return SVN_NO_ERROR;

  /* Get the txn name and hooks environment if they will be needed. */
  if (pb->use_pre_commit_hook || pb->use_post_commit_hook)
    {
      SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, pb->repos->hooks_env_path,
                                         rb->pool, rb->pool));

      err = svn_fs_txn_name(&txn_name, rb->txn, rb->pool);
      if (err)
        {
          svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
          return svn_error_trace(err);
        }
    }

  /* Run the pre-commit hook, if so commanded. */
  if (pb->use_pre_commit_hook)
    {
      err = svn_repos__hooks_pre_commit(pb->repos, hooks_env,
                                        txn_name, rb->pool);
      if (err)
        {
          svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
          return svn_error_trace(err);
        }
    }

  /* Commit. */
  err = svn_fs_commit_txn(&conflict_msg, &committed_rev, rb->txn, rb->pool);
  if (SVN_IS_VALID_REVNUM(committed_rev))
    {
      if (err)
        {
          /* ### Log any error, but better yet is to rev
             ### close_revision()'s API to allow both committed_rev and err
             ### to be returned, see #3768. */
          svn_error_clear(err);
        }
    }
  else
    {
      svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
      if (conflict_msg)
        return svn_error_quick_wrap(err, conflict_msg);
      else
        return svn_error_trace(err);
    }

  /* Run post-commit hook, if so commanded.  */
  if (pb->use_post_commit_hook)
    {
      if ((err = svn_repos__hooks_post_commit(pb->repos, hooks_env,
                                              committed_rev, txn_name,
                                              rb->pool)))
        return svn_error_create
          (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err,
           _("Commit succeeded, but post-commit hook failed"));
    }

  /* After a successful commit, must record the dump-rev -> in-repos-rev
     mapping, so that copyfrom instructions in the dump file can look up the
     correct repository revision to copy from. */
  set_revision_mapping(pb->rev_map, rb->rev, committed_rev);

  /* If the incoming dump stream has non-contiguous revisions (e.g. from
     using svndumpfilter --drop-empty-revs without --renumber-revs) then
     we must account for the missing gaps in PB->REV_MAP.  Otherwise we
     might not be able to map all mergeinfo source revisions to the correct
     revisions in the target repos. */
  if ((pb->last_rev_mapped != SVN_INVALID_REVNUM)
      && (rb->rev != pb->last_rev_mapped + 1))
    {
      svn_revnum_t i;

      for (i = pb->last_rev_mapped + 1; i < rb->rev; i++)
        {
          set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped);
        }
    }

  /* Update our "last revision mapped". */
  pb->last_rev_mapped = rb->rev;

  /* Deltify the predecessors of paths changed in this revision. */
  SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->pool));

  /* Grrr, svn_fs_commit_txn rewrites the datestamp property to the
     current clock-time.  We don't want that, we want to preserve
     history exactly.  Good thing revision props aren't versioned!
     Note that if rb->datestamp is NULL, that's fine -- if the dump
     data doesn't carry a datestamp, we want to preserve that fact in
     the load. */
  SVN_ERR(change_rev_prop(pb->repos, committed_rev, SVN_PROP_REVISION_DATE,
                          rb->datestamp, pb->validate_props, rb->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_txn_committed,
                                        pb->notify_pool);

      notify->new_revision = committed_rev;
      notify->old_revision = ((committed_rev == rb->rev)
                                    ? SVN_INVALID_REVNUM
                                    : rb->rev);
      pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
      svn_pool_clear(pb->notify_pool);
    }

  return SVN_NO_ERROR;
}
Пример #8
0
svn_error_t *
svn_repos__get_commit_ev2(svn_editor_t **editor,
                          svn_repos_t *repos,
                          svn_authz_t *authz,
                          const char *authz_repos_name,
                          const char *authz_user,
                          apr_hash_t *revprops,
                          svn_commit_callback2_t commit_cb,
                          void *commit_baton,
                          svn_cancel_func_t cancel_func,
                          void *cancel_baton,
                          apr_pool_t *result_pool,
                          apr_pool_t *scratch_pool)
{
  static const svn_editor_cb_many_t editor_cbs = {
    add_directory_cb,
    add_file_cb,
    add_symlink_cb,
    add_absent_cb,
    alter_directory_cb,
    alter_file_cb,
    alter_symlink_cb,
    delete_cb,
    copy_cb,
    move_cb,
    complete_cb,
    abort_cb
  };
  struct ev2_baton *eb;
  const svn_string_t *author;
  apr_hash_t *hooks_env;

  /* Parse the hooks-env file (if any). */
  SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
                                     scratch_pool, scratch_pool));

  /* Can the user modify the repository at all?  */
  /* ### check against AUTHZ.  */

  author = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR);

  eb = apr_palloc(result_pool, sizeof(*eb));
  eb->repos = repos;
  eb->authz = authz;
  eb->authz_repos_name = authz_repos_name;
  eb->authz_user = authz_user;
  eb->commit_cb = commit_cb;
  eb->commit_baton = commit_baton;

  SVN_ERR(svn_fs__editor_create(&eb->inner, &eb->txn_name,
                                repos->fs, SVN_FS_TXN_CHECK_LOCKS,
                                cancel_func, cancel_baton,
                                result_pool, scratch_pool));

  /* The TXN has been created. Go ahead and apply all revision properties.  */
  SVN_ERR(apply_revprops(repos->fs, eb->txn_name, revprops, scratch_pool));

  /* Okay... some access is allowed. Let's run the start-commit hook.  */
  SVN_ERR(svn_repos__hooks_start_commit(repos, hooks_env,
                                        author ? author->data : NULL,
                                        repos->client_capabilities,
                                        eb->txn_name, scratch_pool));

  /* Wrap the FS editor within our editor.  */
  SVN_ERR(svn_editor_create(editor, eb, cancel_func, cancel_baton,
                            result_pool, scratch_pool));
  SVN_ERR(svn_editor_setcb_many(*editor, &editor_cbs, scratch_pool));

  return SVN_NO_ERROR;
}
Пример #9
0
svn_error_t *
svn_repos_fs_change_rev_prop4(svn_repos_t *repos,
                              svn_revnum_t rev,
                              const char *author,
                              const char *name,
                              const svn_string_t *const *old_value_p,
                              const svn_string_t *new_value,
                              svn_boolean_t use_pre_revprop_change_hook,
                              svn_boolean_t use_post_revprop_change_hook,
                              svn_repos_authz_func_t authz_read_func,
                              void *authz_read_baton,
                              apr_pool_t *pool)
{
    svn_repos_revision_access_level_t readability;

    SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev,
                                            authz_read_func, authz_read_baton,
                                            pool));

    if (readability == svn_repos_revision_access_full)
    {
        const svn_string_t *old_value;
        char action;
        apr_hash_t *hooks_env;

        SVN_ERR(svn_repos__validate_prop(name, new_value, pool));

        /* Fetch OLD_VALUE for svn_fs_change_rev_prop2(). */
        if (old_value_p)
        {
            old_value = *old_value_p;
        }
        else
        {
            /* Get OLD_VALUE anyway, in order for ACTION and OLD_VALUE arguments
             * to the hooks to be accurate. */
            svn_string_t *old_value2;

            SVN_ERR(svn_fs_revision_prop(&old_value2, repos->fs, rev, name, pool));
            old_value = old_value2;
        }

        /* Prepare ACTION. */
        if (! new_value)
            action = 'D';
        else if (! old_value)
            action = 'A';
        else
            action = 'M';

        /* Parse the hooks-env file (if any, and if to be used). */
        if (use_pre_revprop_change_hook || use_post_revprop_change_hook)
            SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path,
                                               pool, pool));

        /* ### currently not passing the old_value to hooks */
        if (use_pre_revprop_change_hook)
            SVN_ERR(svn_repos__hooks_pre_revprop_change(repos, hooks_env, rev,
                    author, name, new_value,
                    action, pool));

        SVN_ERR(svn_fs_change_rev_prop2(repos->fs, rev, name,
                                        &old_value, new_value, pool));

        if (use_post_revprop_change_hook)
            SVN_ERR(svn_repos__hooks_post_revprop_change(repos, hooks_env, rev,
                    author, name, old_value,
                    action, pool));
    }
    else  /* rev is either unreadable or only partially readable */
    {
        return svn_error_createf
               (SVN_ERR_AUTHZ_UNREADABLE, NULL,
                _("Write denied:  not authorized to read all of revision %ld"), rev);
    }

    return SVN_NO_ERROR;
}