Example #1
0
svn_error_t *
svn_fs_fs__dag_finalize_edits(dag_node_t *file,
                              const svn_checksum_t *checksum,
                              apr_pool_t *pool)
{
    if (checksum)
    {
        svn_checksum_t *file_checksum;

        SVN_ERR(svn_fs_fs__dag_file_checksum(&file_checksum, file,
                                             checksum->kind, pool));
        if (!svn_checksum_match(checksum, file_checksum))
            return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH, NULL,
                                     _("Checksum mismatch, file '%s':\n"
                                       "   expected:  %s\n"
                                       "     actual:  %s\n"),
                                     file->created_path,
                                     svn_checksum_to_cstring_display(checksum,
                                             pool),
                                     svn_checksum_to_cstring_display(file_checksum,
                                             pool));
    }

    return SVN_NO_ERROR;
}
Example #2
0
static svn_error_t *
test_checksum_parse(apr_pool_t *pool)
{
  const char *md5_digest = "8518b76f7a45fe4de2d0955085b41f98";
  const char *sha1_digest = "74d82379bcc6771454377db03b912c2b62704139";
  const char *checksum_display;
  svn_checksum_t *checksum;

  SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_md5, md5_digest, pool));
  checksum_display = svn_checksum_to_cstring_display(checksum, pool);

  if (strcmp(checksum_display, md5_digest) != 0)
    return svn_error_createf
      (SVN_ERR_CHECKSUM_MISMATCH, NULL,
       "verify-checksum: md5 checksum mismatch:\n"
       "   expected:  %s\n"
       "     actual:  %s\n", md5_digest, checksum_display);

  SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, sha1_digest,
                                 pool));
  checksum_display = svn_checksum_to_cstring_display(checksum, pool);

  if (strcmp(checksum_display, sha1_digest) != 0)
    return svn_error_createf
      (SVN_ERR_CHECKSUM_MISMATCH, NULL,
       "verify-checksum: sha1 checksum mismatch:\n"
       "   expected:  %s\n"
       "     actual:  %s\n", sha1_digest, checksum_display);

  return SVN_NO_ERROR;
}
Example #3
0
/* Print REPS_REF_COUNT (a hash as for process_one_revision())
 * to stdout in "refcount => sha1" format.  A sha1 may appear
 * more than once if not all its instances are shared.  Prepend
 * each line by NAME.
 *
 * Use SCRATCH_POOL for temporary allocations.
 */
static svn_error_t *
pretty_print(const char *name,
             apr_hash_t *reps_ref_counts,
             apr_pool_t *scratch_pool)
{
  apr_hash_index_t *hi;

  if (reps_ref_counts == NULL)
    return SVN_NO_ERROR;

  for (hi = apr_hash_first(scratch_pool, reps_ref_counts);
       hi; hi = apr_hash_next(hi))
    {
      struct value_t *value;

      SVN_ERR(cancel_func(NULL));

      value = svn__apr_hash_index_val(hi);
      SVN_ERR(svn_cmdline_printf(scratch_pool, "%s %" APR_UINT64_T_FMT " %s\n",
                                 name, value->refcount,
                                 svn_checksum_to_cstring_display(
                                   value->sha1_checksum,
                                   scratch_pool)));
    }

  return SVN_NO_ERROR;
}
Example #4
0
/* Escape ACTIVITY_ID to be safely usable as a filename.  Simply
   returns the MD5 checksum of the id.
*/
static const char *
escape_activity(const char *activity_id, apr_pool_t *pool)
{
  svn_checksum_t *checksum;
  svn_error_clear(svn_checksum(&checksum, svn_checksum_md5, activity_id,
                               strlen(activity_id), pool));
  return svn_checksum_to_cstring_display(checksum, pool);
}
Example #5
0
/* This function's caller ignores most errors it returns.
   If you extend this function, check the callsite to see if you have
   to make it not-ignore additional error codes.  */
svn_error_t *
svn_fs_fs__get_rep_reference(representation_t **rep,
                             svn_fs_t *fs,
                             svn_checksum_t *checksum,
                             apr_pool_t *pool)
{
  fs_fs_data_t *ffd = fs->fsap_data;
  svn_sqlite__stmt_t *stmt;
  svn_boolean_t have_row;

  SVN_ERR_ASSERT(ffd->rep_sharing_allowed);
  if (! ffd->rep_cache_db)
    SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));

  /* We only allow SHA1 checksums in this table. */
  if (checksum->kind != svn_checksum_sha1)
    return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
                            _("Only SHA1 checksums can be used as keys in the "
                              "rep_cache table.\n"));

  SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_GET_REP));
  SVN_ERR(svn_sqlite__bindf(stmt, "s",
                            svn_checksum_to_cstring(checksum, pool)));

  SVN_ERR(svn_sqlite__step(&have_row, stmt));
  if (have_row)
    {
      *rep = apr_pcalloc(pool, sizeof(**rep));
      svn_fs_fs__id_txn_reset(&(*rep)->txn_id);
      memcpy((*rep)->sha1_digest, checksum->digest,
             sizeof((*rep)->sha1_digest));
      (*rep)->has_sha1 = TRUE;
      (*rep)->revision = svn_sqlite__column_revnum(stmt, 0);
      (*rep)->item_index = svn_sqlite__column_int64(stmt, 1);
      (*rep)->size = svn_sqlite__column_int64(stmt, 2);
      (*rep)->expanded_size = svn_sqlite__column_int64(stmt, 3);
    }
  else
    *rep = NULL;

  SVN_ERR(svn_sqlite__reset(stmt));

  if (*rep)
    {
      /* Check that REP refers to a revision that exists in FS. */
      svn_error_t *err = svn_fs_fs__ensure_revision_exists((*rep)->revision,
                                                           fs, pool);
      if (err)
        return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
                                 "Checksum '%s' in rep-cache is beyond HEAD",
                                 svn_checksum_to_cstring_display(checksum,
                                                                 pool));
    }

  return SVN_NO_ERROR;
}
/* Set *DIGEST to the MD5 hash of STR. */
static svn_error_t *
make_digest(const char **digest,
            const char *str,
            apr_pool_t *pool)
{
  svn_checksum_t *checksum;

  SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, str, strlen(str), pool));

  *digest = svn_checksum_to_cstring_display(checksum, pool);
  return SVN_NO_ERROR;
}
svn_error_t *
svn_checksum_mismatch_err(const svn_checksum_t *expected,
                          const svn_checksum_t *actual,
                          apr_pool_t *scratch_pool,
                          const char *fmt,
                          ...)
{
  va_list ap;
  const char *desc;

  va_start(ap, fmt);
  desc = apr_pvsprintf(scratch_pool, fmt, ap);
  va_end(ap);

  return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH, NULL,
                           _("%s:\n"
                             "   expected:  %s\n"
                             "     actual:  %s\n"),
                desc,
                svn_checksum_to_cstring_display(expected, scratch_pool),
                svn_checksum_to_cstring_display(actual, scratch_pool));
}
Example #8
0
/* Return a textual representation of the DIGEST of given KIND.
 * If IS_NULL is TRUE, no digest is available.
 * Allocate the result in RESULT_POOL.
 */
static const char *
format_digest(const unsigned char *digest,
              svn_checksum_kind_t kind,
              svn_boolean_t is_null,
              apr_pool_t *result_pool)
{
  svn_checksum_t checksum;
  checksum.digest = digest;
  checksum.kind = kind;

  if (is_null)
    return "(null)";

  return svn_checksum_to_cstring_display(&checksum, result_pool);
}
Example #9
0
/* Set *MC_KEY to a memcache key for the given key KEY for CACHE, allocated
   in POOL. */
static svn_error_t *
build_key(const char **mc_key,
          memcache_t *cache,
          const void *raw_key,
          apr_pool_t *pool)
{
  const char *encoded_suffix;
  const char *long_key;
  apr_size_t long_key_len;

  if (cache->klen == APR_HASH_KEY_STRING)
    encoded_suffix = svn_path_uri_encode(raw_key, pool);
  else
    {
      const svn_string_t *raw = svn_string_ncreate(raw_key, cache->klen, pool);
      const svn_string_t *encoded = svn_base64_encode_string2(raw, FALSE,
                                                              pool);
      encoded_suffix = encoded->data;
    }

  long_key = apr_pstrcat(pool, "SVN:", cache->prefix, ":", encoded_suffix,
                         (char *)NULL);
  long_key_len = strlen(long_key);

  /* We don't want to have a key that's too big.  If it was going to
     be too big, we MD5 the entire string, then replace the last bit
     with the checksum.  Note that APR_MD5_DIGESTSIZE is for the pure
     binary digest; we have to double that when we convert to hex.

     Every key we use will either be at most
     MEMCACHED_KEY_UNHASHED_LEN bytes long, or be exactly
     MAX_MEMCACHED_KEY_LEN bytes long. */
  if (long_key_len > MEMCACHED_KEY_UNHASHED_LEN)
    {
      svn_checksum_t *checksum;
      SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, long_key, long_key_len,
                           pool));

      long_key = apr_pstrcat(pool,
                             apr_pstrmemdup(pool, long_key,
                                            MEMCACHED_KEY_UNHASHED_LEN),
                             svn_checksum_to_cstring_display(checksum, pool),
                             (char *)NULL);
    }

  *mc_key = long_key;
  return SVN_NO_ERROR;
}
/* Set *CONTENTS to a readable stream from which the pristine text
 * identified by SHA1_CHECKSUM and PRISTINE_ABSPATH can be read from the
 * pristine store of WCROOT.  If SIZE is not null, set *SIZE to the size
 * in bytes of that text. If that text is not in the pristine store,
 * return an error.
 *
 * Even if the pristine text is removed from the store while it is being
 * read, the stream will remain valid and readable until it is closed.
 *
 * Allocate the stream in RESULT_POOL.
 *
 * This function expects to be executed inside a SQLite txn.
 *
 * Implements 'notes/wc-ng/pristine-store' section A-3(d).
 */
static svn_error_t *
pristine_read_txn(svn_stream_t **contents,
                  svn_filesize_t *size,
                  svn_wc__db_wcroot_t *wcroot,
                  const svn_checksum_t *sha1_checksum,
                  const char *pristine_abspath,
                  apr_pool_t *result_pool,
                  apr_pool_t *scratch_pool)
{
  svn_sqlite__stmt_t *stmt;
  svn_boolean_t have_row;

  /* Check that this pristine text is present in the store.  (The presence
   * of the file is not sufficient.) */
  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                    STMT_SELECT_PRISTINE_SIZE));
  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
  SVN_ERR(svn_sqlite__step(&have_row, stmt));

  if (size)
    *size = svn_sqlite__column_int64(stmt, 0);

  SVN_ERR(svn_sqlite__reset(stmt));
  if (! have_row)
    {
      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
                               _("Pristine text '%s' not present"),
                               svn_checksum_to_cstring_display(
                                 sha1_checksum, scratch_pool));
    }

  /* Open the file as a readable stream.  It will remain readable even when
   * deleted from disk; APR guarantees that on Windows as well as Unix.
   *
   * We also don't enable APR_BUFFERED on this file to maximize throughput
   * e.g. for fulltext comparison.  As we use SVN__STREAM_CHUNK_SIZE buffers
   * where needed in streams, there is no point in having another layer of
   * buffers. */
  if (contents)
    {
      apr_file_t *file;
      SVN_ERR(svn_io_file_open(&file, pristine_abspath, APR_READ,
                               APR_OS_DEFAULT, result_pool));
      *contents = svn_stream_from_aprfile2(file, FALSE, result_pool);
    }

  return SVN_NO_ERROR;
}
Example #11
0
svn_error_t *svn_fs_bdb__set_checksum_rep(svn_fs_t *fs,
                                          svn_checksum_t *checksum,
                                          const char *rep_key,
                                          trail_t *trail,
                                          apr_pool_t *pool)
{
  base_fs_data_t *bfd = fs->fsap_data;
  DBT key, value;
  int db_err;

  /* We only allow SHA1 checksums in this table. */
  if (checksum->kind != svn_checksum_sha1)
    return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
                            _("Only SHA1 checksums can be used as keys in the "
                              "checksum-reps table.\n"));

  /* Create a key from our CHECKSUM. */
  svn_fs_base__checksum_to_dbt(&key, checksum);

  /* Check to see if we already have a mapping for CHECKSUM.  If so,
     and the value is the same one we were about to write, that's
     cool -- just do nothing.  If, however, the value is *different*,
     that's a red flag!  */
  svn_fs_base__trail_debug(trail, "checksum-reps", "get");
  db_err = bfd->checksum_reps->get(bfd->checksum_reps, trail->db_txn,
                                   &key, svn_fs_base__result_dbt(&value), 0);
  svn_fs_base__track_dbt(&value, pool);
  if (db_err != DB_NOTFOUND)
    {
      const char *sum_str = svn_checksum_to_cstring_display(checksum, pool);
      return svn_error_createf
        (SVN_ERR_FS_ALREADY_EXISTS, NULL,
         _("Representation key for checksum '%s' exists in filesystem '%s'."),
         sum_str, fs->path);
    }

  /* Create a value from our REP_KEY, and add this record to the table. */
  svn_fs_base__str_to_dbt(&value, rep_key);
  svn_fs_base__trail_debug(trail, "checksum-reps", "put");
  SVN_ERR(BDB_WRAP(fs, _("storing checksum-reps record"),
                   bfd->checksum_reps->put(bfd->checksum_reps, trail->db_txn,
                                           &key, &value, 0)));
  return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_pristine_get_path(const char **pristine_abspath,
                             svn_wc__db_t *db,
                             const char *wri_abspath,
                             const svn_checksum_t *sha1_checksum,
                             apr_pool_t *result_pool,
                             apr_pool_t *scratch_pool)
{
  svn_wc__db_wcroot_t *wcroot;
  const char *local_relpath;
  svn_boolean_t present;

  SVN_ERR_ASSERT(pristine_abspath != NULL);
  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
  SVN_ERR_ASSERT(sha1_checksum != NULL);
  /* ### Transitional: accept MD-5 and look up the SHA-1.  Return an error
   * if the pristine text is not in the store. */
  if (sha1_checksum->kind != svn_checksum_sha1)
    SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, db, wri_abspath,
                                         sha1_checksum,
                                         scratch_pool, scratch_pool));
  SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);

  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
                                             db, wri_abspath,
                                             scratch_pool, scratch_pool));
  VERIFY_USABLE_WCROOT(wcroot);

  SVN_ERR(svn_wc__db_pristine_check(&present, db, wri_abspath, sha1_checksum,
                                    scratch_pool));
  if (! present)
    return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL,
                             _("The pristine text with checksum '%s' was "
                               "not found"),
                             svn_checksum_to_cstring_display(sha1_checksum,
                                                             scratch_pool));

  SVN_ERR(get_pristine_fname(pristine_abspath, wcroot->abspath,
                             sha1_checksum,
                             result_pool, scratch_pool));

  return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_pristine_get_sha1(const svn_checksum_t **sha1_checksum,
                             svn_wc__db_t *db,
                             const char *wri_abspath,
                             const svn_checksum_t *md5_checksum,
                             apr_pool_t *result_pool,
                             apr_pool_t *scratch_pool)
{
  svn_wc__db_wcroot_t *wcroot;
  const char *local_relpath;
  svn_sqlite__stmt_t *stmt;
  svn_boolean_t have_row;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
  SVN_ERR_ASSERT(sha1_checksum != NULL);
  SVN_ERR_ASSERT(md5_checksum->kind == svn_checksum_md5);

  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
                              wri_abspath, scratch_pool, scratch_pool));
  VERIFY_USABLE_WCROOT(wcroot);

  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                    STMT_SELECT_PRISTINE_BY_MD5));
  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, md5_checksum, scratch_pool));
  SVN_ERR(svn_sqlite__step(&have_row, stmt));
  if (!have_row)
    return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
                             _("The pristine text with MD5 checksum '%s' was "
                               "not found"),
                             svn_checksum_to_cstring_display(md5_checksum,
                                                             scratch_pool));

  SVN_ERR(svn_sqlite__column_checksum(sha1_checksum, stmt, 0, result_pool));
  SVN_ERR_ASSERT((*sha1_checksum)->kind == svn_checksum_sha1);

  return svn_error_trace(svn_sqlite__reset(stmt));
}
Example #14
0
svn_error_t * SVNAuthData::cleanup_callback(svn_boolean_t *delete_cred, void *cleanup_baton,
                                            const char *cred_kind, const char *realmstring,
                                            apr_hash_t * hash, apr_pool_t * scratch_pool)
{
    std::tuple<std::vector<std::tuple<CString, CString, SVNAuthDataInfo>>*, std::vector<std::tuple<CString, CString, SVNAuthDataInfo>>> * tupleBaton =
        (std::tuple<std::vector<std::tuple<CString, CString, SVNAuthDataInfo>>*, std::vector<std::tuple<CString, CString, SVNAuthDataInfo>>>*)cleanup_baton;

    auto authList = std::get<0>(*tupleBaton);
    auto delList = std::get<1>(*tupleBaton);

    CString s1, s2;
    if (cred_kind)
        s1 = CUnicodeUtils::GetUnicode(cred_kind);
    if (realmstring)
        s2 = CUnicodeUtils::GetUnicode(realmstring);

    SVNAuthDataInfo authinfodata;

    for (apr_hash_index_t *hi = apr_hash_first(scratch_pool, hash); hi; hi = apr_hash_next(hi))
    {
        const void *vkey;
        void *val;

        apr_hash_this(hi, &vkey, NULL, &val);
        const char * key = (const char*)vkey;
        svn_string_t *value = (svn_string_t *)val;
        if (strcmp(key, SVN_CONFIG_AUTHN_PASSWORD_KEY) == 0)
        {
            CStringA data(value->data, (int)value->len);
            authinfodata.password = CUnicodeUtils::GetUnicode(data);
        }
        else if (strcmp(key, SVN_CONFIG_AUTHN_PASSPHRASE_KEY) == 0)
        {
            CStringA data(value->data, (int)value->len);
            authinfodata.passphrase = CUnicodeUtils::GetUnicode(data);
        }
        else if (strcmp(key, SVN_CONFIG_AUTHN_PASSTYPE_KEY) == 0)
        {
            CStringA data(value->data, (int)value->len);
            authinfodata.passtype = CUnicodeUtils::GetUnicode(data);
        }
        else if (strcmp(key, SVN_CONFIG_AUTHN_USERNAME_KEY) == 0)
        {
            CStringA data(value->data, (int)value->len);
            authinfodata.username = CUnicodeUtils::GetUnicode(data);
        }
        else if (strcmp(key, SVN_CONFIG_AUTHN_ASCII_CERT_KEY) == 0)
        {
            const svn_string_t * der_cert = nullptr;
            svn_x509_certinfo_t * certinfo = nullptr;
            const apr_array_header_t * hostnames = nullptr;
            svn_error_t *err;

            /* Convert header-less PEM to DER by undoing base64 encoding. */
            der_cert = svn_base64_decode_string(value, scratch_pool);

            err = svn_x509_parse_cert(&certinfo, der_cert->data, der_cert->len,
                                      scratch_pool, scratch_pool);
            if (err)
                continue;
            authinfodata.subject = svn_x509_certinfo_get_subject(certinfo, scratch_pool);
            authinfodata.validfrom = svn_time_to_human_cstring(svn_x509_certinfo_get_valid_from(certinfo), scratch_pool);
            authinfodata.validuntil = svn_time_to_human_cstring(svn_x509_certinfo_get_valid_to(certinfo), scratch_pool);
            authinfodata.issuer = svn_x509_certinfo_get_issuer(certinfo, scratch_pool);
            authinfodata.fingerprint = svn_checksum_to_cstring_display(svn_x509_certinfo_get_digest(certinfo), scratch_pool);

            hostnames = svn_x509_certinfo_get_hostnames(certinfo);
            if (hostnames && !apr_is_empty_array(hostnames))
            {
                int i;
                svn_stringbuf_t *buf = svn_stringbuf_create_empty(scratch_pool);
                for (i = 0; i < hostnames->nelts; ++i)
                {
                    const char *hostname = APR_ARRAY_IDX(hostnames, i, const char*);
                    if (i > 0)
                        svn_stringbuf_appendbytes(buf, ", ", 2);
                    svn_stringbuf_appendbytes(buf, hostname, strlen(hostname));
                }
                authinfodata.hostname = buf->data;
            }
        }
        else if (strcmp(key, SVN_CONFIG_AUTHN_FAILURES_KEY) == 0)
svn_error_t *
svn_fs_fs__set_rep_reference(svn_fs_t *fs,
                             representation_t *rep,
                             svn_boolean_t reject_dup,
                             apr_pool_t *pool)
{
  fs_fs_data_t *ffd = fs->fsap_data;
  svn_sqlite__stmt_t *stmt;
  svn_error_t *err;

  SVN_ERR_ASSERT(ffd->rep_sharing_allowed);
  if (! ffd->rep_cache_db)
    SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));

  /* We only allow SHA1 checksums in this table. */
  if (rep->sha1_checksum == NULL)
    return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
                            _("Only SHA1 checksums can be used as keys in the "
                              "rep_cache table.\n"));

  SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_SET_REP));
  SVN_ERR(svn_sqlite__bindf(stmt, "siiii",
                            svn_checksum_to_cstring(rep->sha1_checksum, pool),
                            (apr_int64_t) rep->revision,
                            (apr_int64_t) rep->offset,
                            (apr_int64_t) rep->size,
                            (apr_int64_t) rep->expanded_size));

  err = svn_sqlite__insert(NULL, stmt);
  if (err)
    {
      representation_t *old_rep;

      if (err->apr_err != SVN_ERR_SQLITE_CONSTRAINT)
        return svn_error_trace(err);

      svn_error_clear(err);

      /* Constraint failed so the mapping for SHA1_CHECKSUM->REP
         should exist.  If so, and the value is the same one we were
         about to write, that's cool -- just do nothing.  If, however,
         the value is *different*, that's a red flag!  */
      SVN_ERR(svn_fs_fs__get_rep_reference(&old_rep, fs, rep->sha1_checksum,
                                           pool));

      if (old_rep)
        {
          if (reject_dup && ((old_rep->revision != rep->revision)
                             || (old_rep->offset != rep->offset)
                             || (old_rep->size != rep->size)
                             || (old_rep->expanded_size != rep->expanded_size)))
            return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
                                     apr_psprintf(pool,
                              _("Representation key for checksum '%%s' exists "
                                "in filesystem '%%s' with a different value "
                                "(%%ld,%%%s,%%%s,%%%s) than what we were about "
                                "to store (%%ld,%%%s,%%%s,%%%s)"),
                              APR_OFF_T_FMT, SVN_FILESIZE_T_FMT,
                              SVN_FILESIZE_T_FMT, APR_OFF_T_FMT,
                              SVN_FILESIZE_T_FMT, SVN_FILESIZE_T_FMT),
                 svn_checksum_to_cstring_display(rep->sha1_checksum, pool),
                 fs->path, old_rep->revision, old_rep->offset, old_rep->size,
                 old_rep->expanded_size, rep->revision, rep->offset, rep->size,
                 rep->expanded_size);
          else
            return SVN_NO_ERROR;
        }
      else
        {
          /* Something really odd at this point, we failed to insert the
             checksum AND failed to read an existing checksum.  Do we need
             to flag this? */
        }
    }

  return SVN_NO_ERROR;
}
/* Install the pristine text described by BATON into the pristine store of
 * SDB.  If it is already stored then just delete the new file
 * BATON->tempfile_abspath.
 *
 * This function expects to be executed inside a SQLite txn that has already
 * acquired a 'RESERVED' lock.
 *
 * Implements 'notes/wc-ng/pristine-store' section A-3(a).
 */
static svn_error_t *
pristine_install_txn(svn_sqlite__db_t *sdb,
                     /* The path to the source file that is to be moved into place. */
                     svn_stream_t *install_stream,
                     /* The target path for the file (within the pristine store). */
                     const char *pristine_abspath,
                     /* The pristine text's SHA-1 checksum. */
                     const svn_checksum_t *sha1_checksum,
                     /* The pristine text's MD-5 checksum. */
                     const svn_checksum_t *md5_checksum,
                     apr_pool_t *scratch_pool)
{
  svn_sqlite__stmt_t *stmt;
  svn_boolean_t have_row;

  /* If this pristine text is already present in the store, just keep it:
   * delete the new one and return. */
  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_PRISTINE));
  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
  SVN_ERR(svn_sqlite__step(&have_row, stmt));
  SVN_ERR(svn_sqlite__reset(stmt));

  if (have_row)
    {
#ifdef SVN_DEBUG
      /* Consistency checks.  Verify both files exist and match.
       * ### We could check much more. */
      {
        apr_finfo_t finfo1, finfo2;

        SVN_ERR(svn_stream__install_get_info(&finfo1, install_stream, APR_FINFO_SIZE,
                                             scratch_pool));

        SVN_ERR(svn_io_stat(&finfo2, pristine_abspath, APR_FINFO_SIZE,
                            scratch_pool));
        if (finfo1.size != finfo2.size)
          {
            return svn_error_createf(
              SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
              _("New pristine text '%s' has different size: %s versus %s"),
              svn_checksum_to_cstring_display(sha1_checksum, scratch_pool),
              apr_off_t_toa(scratch_pool, finfo1.size),
              apr_off_t_toa(scratch_pool, finfo2.size));
          }
      }
#endif

      /* Remove the temp file: it's already there */
      SVN_ERR(svn_stream__install_delete(install_stream, scratch_pool));
      return SVN_NO_ERROR;
    }

  /* Move the file to its target location.  (If it is already there, it is
   * an orphan file and it doesn't matter if we overwrite it.) */
  {
    apr_finfo_t finfo;
    SVN_ERR(svn_stream__install_get_info(&finfo, install_stream,
                                         APR_FINFO_SIZE, scratch_pool));
    SVN_ERR(svn_stream__install_stream(install_stream, pristine_abspath,
                                       TRUE, scratch_pool));

    SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_PRISTINE));
    SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
    SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, scratch_pool));
    SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
    SVN_ERR(svn_sqlite__insert(NULL, stmt));

    SVN_ERR(svn_io_set_file_read_only(pristine_abspath, FALSE, scratch_pool));
  }

  return SVN_NO_ERROR;
}