diff options
Diffstat (limited to 'subversion/libsvn_fs_base')
| -rw-r--r-- | subversion/libsvn_fs_base/bdb/changes-table.c | 81 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/bdb/locks-table.c | 2 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/bdb/strings-table.c | 4 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/dag.c | 39 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/dag.h | 2 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/fs.c | 149 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/fs.h | 12 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/id.c | 7 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/id.h | 6 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/key-gen.c | 36 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/key-gen.h | 7 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/libsvn_fs_base.pc.in | 12 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/lock.c | 148 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/lock.h | 27 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/reps-strings.c | 5 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/revs-txns.c | 45 | ||||
| -rw-r--r-- | subversion/libsvn_fs_base/tree.c | 177 | 
17 files changed, 563 insertions, 196 deletions
| diff --git a/subversion/libsvn_fs_base/bdb/changes-table.c b/subversion/libsvn_fs_base/bdb/changes-table.c index 80ff468074e8..b20631863369 100644 --- a/subversion/libsvn_fs_base/bdb/changes-table.c +++ b/subversion/libsvn_fs_base/bdb/changes-table.c @@ -121,12 +121,32 @@ svn_fs_bdb__changes_delete(svn_fs_t *fs,    return SVN_NO_ERROR;  } +/* Return a deep FS API type copy of SOURCE in internal format and allocate + * the result in RESULT_POOL. + */ +static svn_fs_path_change2_t * +change_to_fs_change(const change_t *change, +                    apr_pool_t *result_pool) +{ +  svn_fs_path_change2_t *result = svn_fs__path_change_create_internal( +                                    svn_fs_base__id_copy(change->noderev_id, +                                                         result_pool), +                                    change->kind, +                                    result_pool); +  result->text_mod = change->text_mod; +  result->prop_mod = change->prop_mod; +  result->node_kind = svn_node_unknown; +  result->copyfrom_known = FALSE; + +  return result; +}  /* Merge the internal-use-only CHANGE into a hash of public-FS     svn_fs_path_change2_t CHANGES, collapsing multiple changes into a     single succinct change per path. */  static svn_error_t *  fold_change(apr_hash_t *changes, +            apr_hash_t *deletions,              const change_t *change)  {    apr_pool_t *pool = apr_hash_pool_get(changes); @@ -185,7 +205,7 @@ fold_change(apr_hash_t *changes,          case svn_fs_path_change_reset:            /* A reset here will simply remove the path change from the               hash. */ -          old_change = NULL; +          new_change = NULL;            break;          case svn_fs_path_change_delete: @@ -194,14 +214,21 @@ fold_change(apr_hash_t *changes,                /* If the path was introduced in this transaction via an                   add, and we are deleting it, just remove the path                   altogether. */ -              old_change = NULL; +              new_change = NULL; +            } +          else if (old_change->change_kind == svn_fs_path_change_replace) +            { +              /* A deleting a 'replace' restore the original deletion. */ +              new_change = svn_hash_gets(deletions, path); +              SVN_ERR_ASSERT(new_change);              }            else              {                /* A deletion overrules all previous changes. */ -              old_change->change_kind = svn_fs_path_change_delete; -              old_change->text_mod = change->text_mod; -              old_change->prop_mod = change->prop_mod; +              new_change = old_change; +              new_change->change_kind = svn_fs_path_change_delete; +              new_change->text_mod = change->text_mod; +              new_change->prop_mod = change->prop_mod;              }            break; @@ -209,38 +236,33 @@ fold_change(apr_hash_t *changes,          case svn_fs_path_change_replace:            /* An add at this point must be following a previous delete,               so treat it just like a replace. */ -          old_change->change_kind = svn_fs_path_change_replace; -          old_change->node_rev_id = svn_fs_base__id_copy(change->noderev_id, -                                                         pool); -          old_change->text_mod = change->text_mod; -          old_change->prop_mod = change->prop_mod; + +          new_change = change_to_fs_change(change, pool); +          new_change->change_kind = svn_fs_path_change_replace; + +          /* Remember the original deletion. +           * Make sure to allocate the hash key in a durable pool. */ +          svn_hash_sets(deletions, +                        apr_pstrdup(apr_hash_pool_get(deletions), path), +                        old_change);            break;          case svn_fs_path_change_modify:          default: +          new_change = old_change;            if (change->text_mod) -            old_change->text_mod = TRUE; +            new_change->text_mod = TRUE;            if (change->prop_mod) -            old_change->prop_mod = TRUE; +            new_change->prop_mod = TRUE;            break;          } - -      /* Point our new_change to our (possibly modified) old_change. */ -      new_change = old_change;      }    else      {        /* This change is new to the hash, so make a new public change           structure from the internal one (in the hash's pool), and dup           the path into the hash's pool, too. */ -      new_change = svn_fs__path_change_create_internal( -                       svn_fs_base__id_copy(change->noderev_id, pool), -                       change->kind, -                       pool); -      new_change->text_mod = change->text_mod; -      new_change->prop_mod = change->prop_mod; -      new_change->node_kind = svn_node_unknown; -      new_change->copyfrom_known = FALSE; +      new_change = change_to_fs_change(change, pool);        path = apr_pstrdup(pool, change->path);      } @@ -265,6 +287,8 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,    svn_error_t *err = SVN_NO_ERROR;    apr_hash_t *changes = apr_hash_make(pool);    apr_pool_t *subpool = svn_pool_create(pool); +  apr_pool_t *iterpool = svn_pool_create(pool); +  apr_hash_t *deletions = apr_hash_make(subpool);    /* Get a cursor on the first record matching KEY, and then loop over       the records, adding them to the return array. */ @@ -286,11 +310,11 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,        svn_skel_t *result_skel;        /* Clear the per-iteration subpool. */ -      svn_pool_clear(subpool); +      svn_pool_clear(iterpool);        /* RESULT now contains a change record associated with KEY.  We           need to parse that skel into an change_t structure ...  */ -      result_skel = svn_skel__parse(result.data, result.size, subpool); +      result_skel = svn_skel__parse(result.data, result.size, iterpool);        if (! result_skel)          {            err = svn_error_createf(SVN_ERR_FS_CORRUPT, NULL, @@ -298,12 +322,12 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,                                    key);            goto cleanup;          } -      err = svn_fs_base__parse_change_skel(&change, result_skel, subpool); +      err = svn_fs_base__parse_change_skel(&change, result_skel, iterpool);        if (err)          goto cleanup;        /* ... and merge it with our return hash.  */ -      err = fold_change(changes, change); +      err = fold_change(changes, deletions, change);        if (err)          goto cleanup; @@ -319,7 +343,7 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,          {            apr_hash_index_t *hi; -          for (hi = apr_hash_first(subpool, changes); +          for (hi = apr_hash_first(iterpool, changes);                 hi;                 hi = apr_hash_next(hi))              { @@ -347,6 +371,7 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,      }    /* Destroy the per-iteration subpool. */ +  svn_pool_destroy(iterpool);    svn_pool_destroy(subpool);    /* If there are no (more) change records for this KEY, we're diff --git a/subversion/libsvn_fs_base/bdb/locks-table.c b/subversion/libsvn_fs_base/bdb/locks-table.c index a22663f391e4..e81bca8faa65 100644 --- a/subversion/libsvn_fs_base/bdb/locks-table.c +++ b/subversion/libsvn_fs_base/bdb/locks-table.c @@ -257,7 +257,7 @@ svn_fs_bdb__locks_get(svn_fs_t *fs,                             DB_SET_RANGE);    if (!svn_fspath__is_root(path, strlen(path))) -    lookup_path = apr_pstrcat(pool, path, "/", (char *)NULL); +    lookup_path = apr_pstrcat(pool, path, "/", SVN_VA_NULL);    lookup_len = strlen(lookup_path);    /* As long as the prefix of the returned KEY matches LOOKUP_PATH we diff --git a/subversion/libsvn_fs_base/bdb/strings-table.c b/subversion/libsvn_fs_base/bdb/strings-table.c index f5348e7c1cb2..e1f4b90aa154 100644 --- a/subversion/libsvn_fs_base/bdb/strings-table.c +++ b/subversion/libsvn_fs_base/bdb/strings-table.c @@ -236,9 +236,9 @@ svn_fs_bdb__string_read(svn_fs_t *fs,      {        svn_fs_base__clear_dbt(&result);        result.data = buf + bytes_read; -      result.ulen = *len - bytes_read; +      result.ulen = (u_int32_t)(*len - bytes_read);        result.doff = (u_int32_t)offset; -      result.dlen = *len - bytes_read; +      result.dlen = result.ulen;        result.flags |= (DB_DBT_USERMEM | DB_DBT_PARTIAL);        db_err = svn_bdb_dbc_get(cursor, &query, &result, DB_CURRENT);        if (db_err) diff --git a/subversion/libsvn_fs_base/dag.c b/subversion/libsvn_fs_base/dag.c index 510ccbbc7cc0..e530fb2b4305 100644 --- a/subversion/libsvn_fs_base/dag.c +++ b/subversion/libsvn_fs_base/dag.c @@ -1028,12 +1028,14 @@ svn_fs_base__dag_delete_if_mutable(svn_fs_t *fs,                void *val;                svn_fs_dirent_t *dirent; +              svn_pool_clear(subpool);                apr_hash_this(hi, NULL, NULL, &val);                dirent = val;                SVN_ERR(svn_fs_base__dag_delete_if_mutable(fs, dirent->id,                                                           txn_id, trail,                                                           subpool));              } +          svn_pool_destroy(subpool);          }      } @@ -1342,7 +1344,7 @@ svn_fs_base__dag_finalize_edits(dag_node_t *file,  dag_node_t * -svn_fs_base__dag_dup(dag_node_t *node, +svn_fs_base__dag_dup(const dag_node_t *node,                       apr_pool_t *pool)  {    /* Allocate our new node. */ @@ -1579,10 +1581,10 @@ svn_fs_base__dag_commit_txn(svn_revnum_t *new_rev,                              apr_pool_t *pool)  {    revision_t revision; -  svn_string_t date;    apr_hash_t *txnprops;    svn_fs_t *fs = txn->fs;    const char *txn_id = txn->id; +  const svn_string_t *client_date;    /* Remove any temporary transaction properties initially created by       begin_txn().  */ @@ -1601,16 +1603,27 @@ svn_fs_base__dag_commit_txn(svn_revnum_t *new_rev,      SVN_ERR(svn_fs_base__set_txn_prop              (fs, txn_id, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL, trail, pool)); +  client_date = svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE); +  if (client_date) +    SVN_ERR(svn_fs_base__set_txn_prop +            (fs, txn_id, SVN_FS__PROP_TXN_CLIENT_DATE, NULL, trail, pool)); +    /* Promote the unfinished transaction to a committed one. */    SVN_ERR(svn_fs_base__txn_make_committed(fs, txn_id, *new_rev,                                            trail, pool)); -  /* Set a date on the commit.  We wait until now to fetch the date, -     so it's definitely newer than any previous revision's date. */ -  date.data = svn_time_to_cstring(apr_time_now(), pool); -  date.len = strlen(date.data); -  return svn_fs_base__set_rev_prop(fs, *new_rev, SVN_PROP_REVISION_DATE, -                                   NULL, &date, trail, pool); +  if (!client_date || strcmp(client_date->data, "1")) +    { +      /* Set a date on the commit if requested.  We wait until now to fetch the +         date, so it's definitely newer than any previous revision's date. */ +      svn_string_t date; +      date.data = svn_time_to_cstring(apr_time_now(), pool); +      date.len = strlen(date.data); +      SVN_ERR(svn_fs_base__set_rev_prop(fs, *new_rev, SVN_PROP_REVISION_DATE, +                                        NULL, &date, trail, pool)); +    } + +  return SVN_NO_ERROR;  } @@ -1644,14 +1657,8 @@ svn_fs_base__things_different(svn_boolean_t *props_changed,    /* Compare contents keys and their (optional) uniquifiers. */    if (contents_changed != NULL) -    *contents_changed = -      (! (svn_fs_base__same_keys(noderev1->data_key, -                                 noderev2->data_key) -          /* Technically, these uniquifiers aren't used and "keys", -             but keys are base-36 stringified numbers, so we'll take -             this liberty. */ -          && (svn_fs_base__same_keys(noderev1->data_key_uniquifier, -                                     noderev2->data_key_uniquifier)))); +    *contents_changed = (! svn_fs_base__same_keys(noderev1->data_key, +                                                  noderev2->data_key));    return SVN_NO_ERROR;  } diff --git a/subversion/libsvn_fs_base/dag.h b/subversion/libsvn_fs_base/dag.h index 4c50c8441299..fb963ce66451 100644 --- a/subversion/libsvn_fs_base/dag.h +++ b/subversion/libsvn_fs_base/dag.h @@ -82,7 +82,7 @@ svn_error_t *svn_fs_base__dag_get_node(dag_node_t **node,  /* Return a new dag_node_t object referring to the same node as NODE,     allocated in POOL.  */ -dag_node_t *svn_fs_base__dag_dup(dag_node_t *node, +dag_node_t *svn_fs_base__dag_dup(const dag_node_t *node,                                   apr_pool_t *pool); diff --git a/subversion/libsvn_fs_base/fs.c b/subversion/libsvn_fs_base/fs.c index 4ad9e6f72be2..06dfbaf2f419 100644 --- a/subversion/libsvn_fs_base/fs.c +++ b/subversion/libsvn_fs_base/fs.c @@ -65,8 +65,6 @@  #include "../libsvn_fs/fs-loader.h"  #include "private/svn_fs_util.h" -#include "private/svn_subr_private.h" -  /* Checking for return values, and reporting errors.  */ @@ -473,6 +471,59 @@ bdb_write_config(svn_fs_t *fs)  }  static svn_error_t * +base_bdb_info_format(int *fs_format, +                     svn_version_t **supports_version, +                     svn_fs_t *fs, +                     apr_pool_t *result_pool, +                     apr_pool_t *scratch_pool) +{ +  base_fs_data_t *bfd = fs->fsap_data; + +  *fs_format = bfd->format; +  *supports_version = apr_palloc(result_pool, sizeof(svn_version_t)); + +  (*supports_version)->major = SVN_VER_MAJOR; +  (*supports_version)->minor = 0; +  (*supports_version)->patch = 0; +  (*supports_version)->tag = ""; + +  switch (bfd->format) +    { +    case 1: +      break; +    case 2: +      (*supports_version)->minor = 4; +      break; +    case 3: +      (*supports_version)->minor = 5; +      break; +    case 4: +      (*supports_version)->minor = 6; +      break; +#ifdef SVN_DEBUG +# if SVN_FS_BASE__FORMAT_NUMBER != 4 +#  error "Need to add a 'case' statement here" +# endif +#endif +    } + +  return SVN_NO_ERROR; +} + +static svn_error_t * +base_bdb_info_config_files(apr_array_header_t **files, +                           svn_fs_t *fs, +                           apr_pool_t *result_pool, +                           apr_pool_t *scratch_pool) +{ +  *files = apr_array_make(result_pool, 1, sizeof(const char *)); +  APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path, +                                                         BDB_CONFIG_FILE, +                                                         result_pool); +  return SVN_NO_ERROR; +} + +static svn_error_t *  base_bdb_verify_root(svn_fs_root_t *root,                       apr_pool_t *scratch_pool)  { @@ -509,6 +560,9 @@ static fs_vtable_t fs_vtable = {    svn_fs_base__unlock,    svn_fs_base__get_lock,    svn_fs_base__get_locks, +  base_bdb_info_format, +  base_bdb_info_config_files, +  NULL /* info_fsap */,    base_bdb_verify_root,    base_bdb_freeze,    base_bdb_set_errcall, @@ -675,7 +729,10 @@ populate_opened_fs(svn_fs_t *fs, apr_pool_t *scratch_pool)  }  static svn_error_t * -base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool, +base_create(svn_fs_t *fs, +            const char *path, +            svn_mutex__t *common_pool_lock, +            apr_pool_t *pool,              apr_pool_t *common_pool)  {    int format = SVN_FS_BASE__FORMAT_NUMBER; @@ -684,12 +741,27 @@ base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,    /* See if compatibility with older versions was explicitly requested. */    if (fs->config)      { -      if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE)) -        format = 1; -      else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE)) -        format = 2; -      else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE)) -        format = 3; +      svn_version_t *compatible_version; +      SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config, +                                         pool)); + +      /* select format number */ +      switch(compatible_version->minor) +        { +          case 0: +          case 1: +          case 2: +          case 3: format = 1; +                  break; + +          case 4: format = 2; +                  break; + +          case 5: format = 3; +                  break; + +          default:format = SVN_FS_BASE__FORMAT_NUMBER; +        }      }    /* Create the environment and databases. */ @@ -711,8 +783,8 @@ base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,    return SVN_NO_ERROR;;  error: -  svn_error_clear(cleanup_fs(fs)); -  return svn_err; +  return svn_error_compose_create(svn_err, +                                  svn_error_trace(cleanup_fs(fs)));  } @@ -751,7 +823,10 @@ check_format(int format)  }  static svn_error_t * -base_open(svn_fs_t *fs, const char *path, apr_pool_t *pool, +base_open(svn_fs_t *fs, +          const char *path, +          svn_mutex__t *common_pool_lock, +          apr_pool_t *pool,            apr_pool_t *common_pool)  {    int format; @@ -796,8 +871,8 @@ base_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,    return SVN_NO_ERROR;   error: -  svn_error_clear(cleanup_fs(fs)); -  return svn_err; +  return svn_error_compose_create(svn_err, +                                  svn_error_trace(cleanup_fs(fs)));  } @@ -834,7 +909,10 @@ bdb_recover(const char *path, svn_boolean_t fatal, apr_pool_t *pool)  }  static svn_error_t * -base_open_for_recovery(svn_fs_t *fs, const char *path, apr_pool_t *pool, +base_open_for_recovery(svn_fs_t *fs, +                       const char *path, +                       svn_mutex__t *common_pool_lock, +                       apr_pool_t *pool,                         apr_pool_t *common_pool)  {    /* Just stash the path in the fs pointer - it's all we really need. */ @@ -844,7 +922,14 @@ base_open_for_recovery(svn_fs_t *fs, const char *path, apr_pool_t *pool,  }  static svn_error_t * -base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool, +base_upgrade(svn_fs_t *fs, +             const char *path, +             svn_fs_upgrade_notify_t notify_func, +             void *notify_baton, +             svn_cancel_func_t cancel_func, +             void *cancel_baton, +             svn_mutex__t *common_pool_lock, +             apr_pool_t *pool,               apr_pool_t *common_pool)  {    const char *version_file_path; @@ -867,6 +952,9 @@ base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,    /* Bump the format file's stored version number. */    SVN_ERR(svn_io_write_version_file(version_file_path,                                      SVN_FS_BASE__FORMAT_NUMBER, pool)); +  if (notify_func) +    SVN_ERR(notify_func(notify_baton, SVN_FS_BASE__FORMAT_NUMBER, +                        svn_fs_upgrade_format_bumped, pool));    /* Check and see if we need to record the "bump" revision. */    if (old_format_number < SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT) @@ -883,7 +971,7 @@ base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,           But it's better to use the existing encapsulation of "opening           the filesystem" rather than duplicating (or worse, partially           duplicating) that logic here.  */ -      SVN_ERR(base_open(fs, path, subpool, common_pool)); +      SVN_ERR(base_open(fs, path, common_pool_lock, subpool, common_pool));        /* Fetch the youngest rev, and record it */        SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, subpool)); @@ -905,6 +993,7 @@ base_verify(svn_fs_t *fs, const char *path,              void *notify_baton,              svn_cancel_func_t cancel_func,              void *cancel_baton, +            svn_mutex__t *common_pool_lock,              apr_pool_t *pool,              apr_pool_t *common_pool)  { @@ -929,6 +1018,7 @@ base_bdb_pack(svn_fs_t *fs,                void *notify_baton,                svn_cancel_func_t cancel,                void *cancel_baton, +              svn_mutex__t *common_pool_lock,                apr_pool_t *pool,                apr_pool_t *common_pool)  { @@ -994,7 +1084,7 @@ svn_fs_base__clean_logs(const char *live_path,    {  /* Process unused logs from live area */      int idx; -    apr_pool_t *sub_pool = svn_pool_create(pool); +    apr_pool_t *subpool = svn_pool_create(pool);      /* Process log files. */      for (idx = 0; idx < logfiles->nelts; idx++) @@ -1003,9 +1093,9 @@ svn_fs_base__clean_logs(const char *live_path,          const char *live_log_path;          const char *backup_log_path; -        svn_pool_clear(sub_pool); -        live_log_path = svn_dirent_join(live_path, log_file, sub_pool); -        backup_log_path = svn_dirent_join(backup_path, log_file, sub_pool); +        svn_pool_clear(subpool); +        live_log_path = svn_dirent_join(live_path, log_file, subpool); +        backup_log_path = svn_dirent_join(backup_path, log_file, subpool);          { /* Compare files. No point in using MD5 and wasting CPU cycles as we               got full copies of both logs */ @@ -1022,17 +1112,17 @@ svn_fs_base__clean_logs(const char *live_path,              SVN_ERR(svn_io_files_contents_same_p(&files_match,                                                   live_log_path,                                                   backup_log_path, -                                                 sub_pool)); +                                                 subpool));            /* If log files do not match, go to the next log file. */            if (!files_match)              continue;          } -        SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, sub_pool)); +        SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, subpool));        } -    svn_pool_destroy(sub_pool); +    svn_pool_destroy(subpool);    }    return SVN_NO_ERROR; @@ -1201,9 +1291,13 @@ base_hotcopy(svn_fs_t *src_fs,               const char *dest_path,               svn_boolean_t clean_logs,               svn_boolean_t incremental, +             svn_fs_hotcopy_notify_t notify_func, +             void *notify_baton,               svn_cancel_func_t cancel_func,               void *cancel_baton, -             apr_pool_t *pool) +             svn_mutex__t *common_pool_lock, +             apr_pool_t *pool, +             apr_pool_t *common_pool)  {    svn_error_t *err;    u_int32_t pagesize; @@ -1388,6 +1482,7 @@ base_set_svn_fs_open(svn_fs_t *fs,                       svn_error_t *(*svn_fs_open_)(svn_fs_t **,                                                    const char *,                                                    apr_hash_t *, +                                                  apr_pool_t *,                                                    apr_pool_t *))  {    return SVN_NO_ERROR; @@ -1409,7 +1504,8 @@ static fs_library_vtable_t library_vtable = {    base_bdb_pack,    base_bdb_logfiles,    svn_fs_base__id_parse, -  base_set_svn_fs_open +  base_set_svn_fs_open, +  NULL /* info_fsap_dup */  };  svn_error_t * @@ -1420,6 +1516,7 @@ svn_fs_base__init(const svn_version_t *loader_version,      {        { "svn_subr",  svn_subr_version },        { "svn_delta", svn_delta_version }, +      { "svn_fs_util", svn_fs_util__version },        { NULL, NULL }      }; diff --git a/subversion/libsvn_fs_base/fs.h b/subversion/libsvn_fs_base/fs.h index 017c89856f40..bdbced3ccb49 100644 --- a/subversion/libsvn_fs_base/fs.h +++ b/subversion/libsvn_fs_base/fs.h @@ -43,7 +43,11 @@ extern "C" {     repository format number, and independent of any other FS back     ends.  See the SVN_FS_BASE__MIN_*_FORMAT defines to get a sense of     what changes and features were added in which versions of this -   back-end's format.  */ +   back-end's format. + +   Note: If you bump this, please update the switch statement in +         base_create() as well. + */  #define SVN_FS_BASE__FORMAT_NUMBER                4  /* Minimum format number that supports representation sharing.  This @@ -191,7 +195,11 @@ typedef struct node_revision_t       only because one or both of us decided to pick up a shared       representation after-the-fact."  May be NULL (if this node       revision isn't using a shared rep, or isn't the original -     "assignee" of a shared rep). */ +     "assignee" of a shared rep). + +     This is no longer used by the 1.9 code but we have to keep +     reading and writing it to remain compatible with 1.8, and +     earlier, that require it. */    const char *data_key_uniquifier;    /* representation key for this node's text-data-in-progess (files diff --git a/subversion/libsvn_fs_base/id.c b/subversion/libsvn_fs_base/id.c index c063d02e5e6b..a0947430ae5c 100644 --- a/subversion/libsvn_fs_base/id.c +++ b/subversion/libsvn_fs_base/id.c @@ -108,13 +108,14 @@ svn_fs_base__id_check_related(const svn_fs_id_t *a,  } -int +svn_fs_node_relation_t  svn_fs_base__id_compare(const svn_fs_id_t *a,                          const svn_fs_id_t *b)  {    if (svn_fs_base__id_eq(a, b)) -    return 0; -  return (svn_fs_base__id_check_related(a, b) ? 1 : -1); +    return svn_fs_node_unchanged; +  return (svn_fs_base__id_check_related(a, b) ? svn_fs_node_common_ancestor +                                              : svn_fs_node_unrelated);  } diff --git a/subversion/libsvn_fs_base/id.h b/subversion/libsvn_fs_base/id.h index 4cdb45c92b45..9cdc46df2a56 100644 --- a/subversion/libsvn_fs_base/id.h +++ b/subversion/libsvn_fs_base/id.h @@ -53,9 +53,9 @@ svn_boolean_t svn_fs_base__id_eq(const svn_fs_id_t *a,  svn_boolean_t svn_fs_base__id_check_related(const svn_fs_id_t *a,                                              const svn_fs_id_t *b); -/* Return 0 if A and B are equal, 1 if they are related, -1 otherwise. */ -int svn_fs_base__id_compare(const svn_fs_id_t *a, -                            const svn_fs_id_t *b); +/* Return the noderev relationship between A and B. */ +svn_fs_node_relation_t svn_fs_base__id_compare(const svn_fs_id_t *a, +                                               const svn_fs_id_t *b);  /* Create an ID based on NODE_ID, COPY_ID, and TXN_ID, allocated in     POOL. */ diff --git a/subversion/libsvn_fs_base/key-gen.c b/subversion/libsvn_fs_base/key-gen.c index 411207d50016..34f0e0f51069 100644 --- a/subversion/libsvn_fs_base/key-gen.c +++ b/subversion/libsvn_fs_base/key-gen.c @@ -39,20 +39,19 @@ void  svn_fs_base__next_key(const char *this, apr_size_t *len, char *next)  {    apr_size_t olen = *len;     /* remember the first length */ -  int i = olen - 1;           /* initial index; we work backwards */ +  apr_size_t i;               /* current index */    char c;                     /* current char */    svn_boolean_t carry = TRUE; /* boolean: do we have a carry or not?                                   We start with a carry, because we're                                   incrementing the number, after all. */ -  /* Leading zeros are not allowed, except for the string "0". */ -  if ((*len > 1) && (this[0] == '0')) -    { -      *len = 0; -      return; -    } +  /* Empty strings and leading zeros (except for the string "0") are not +   * allowed.  Run our malfunction handler to prevent possible db corruption +   * from being propagated further. */ +  SVN_ERR_ASSERT_NO_RETURN(olen != 0 && (olen == 1 || this[0] != '0')); -  for (i = (olen - 1); i >= 0; i--) +  i = olen - 1; /* initial index: we work backwords */ +  while (1729)      {        c = this[i]; @@ -79,6 +78,11 @@ svn_fs_base__next_key(const char *this, apr_size_t *len, char *next)          }        else          next[i] = c; + +      if (i == 0) +        break; + +      i--;      }    /* The new length is OLEN, plus 1 if there's a carry out of the @@ -102,22 +106,6 @@ svn_fs_base__next_key(const char *this, apr_size_t *len, char *next)  } -int -svn_fs_base__key_compare(const char *a, const char *b) -{ -  int a_len = strlen(a); -  int b_len = strlen(b); -  int cmp; - -  if (a_len > b_len) -    return 1; -  if (b_len > a_len) -    return -1; -  cmp = strcmp(a, b); -  return (cmp ? (cmp / abs(cmp)) : 0); -} - -  svn_boolean_t  svn_fs_base__same_keys(const char *a, const char *b)  { diff --git a/subversion/libsvn_fs_base/key-gen.h b/subversion/libsvn_fs_base/key-gen.h index e1cd1aa049ab..a0634d347289 100644 --- a/subversion/libsvn_fs_base/key-gen.h +++ b/subversion/libsvn_fs_base/key-gen.h @@ -78,13 +78,6 @@ extern "C" {  void svn_fs_base__next_key(const char *this, apr_size_t *len, char *next); -/* Compare two strings A and B as base-36 alphanumeric keys. - * - * Return -1, 0, or 1 if A is less than, equal to, or greater than B, - * respectively. - */ -int svn_fs_base__key_compare(const char *a, const char *b); -  /* Compare two strings A and B as base-36 alphanumber keys.   *   * Return TRUE iff both keys are NULL or both keys have the same diff --git a/subversion/libsvn_fs_base/libsvn_fs_base.pc.in b/subversion/libsvn_fs_base/libsvn_fs_base.pc.in new file mode 100644 index 000000000000..ef44eafca7a7 --- /dev/null +++ b/subversion/libsvn_fs_base/libsvn_fs_base.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsvn_fs_base +Description: Subversion Filesystem Base Library +Version: @PACKAGE_VERSION@ +Requires:  apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@ +Requires.private:  libsvn_delta libsvn_subr libsvn_fs_util +Libs: -L${libdir}  -lsvn_fs_base @SVN_DB_LIBS@ +Cflags: -I${includedir} diff --git a/subversion/libsvn_fs_base/lock.c b/subversion/libsvn_fs_base/lock.c index 79f72ccf7aee..1145207ea38c 100644 --- a/subversion/libsvn_fs_base/lock.c +++ b/subversion/libsvn_fs_base/lock.c @@ -39,6 +39,7 @@  #include "private/svn_fs_util.h"  #include "private/svn_subr_private.h"  #include "private/svn_dep_compat.h" +#include "revs-txns.h"  /* Add LOCK and its associated LOCK_TOKEN (associated with PATH) as @@ -70,6 +71,7 @@ delete_lock_and_token(const char *lock_token,  } +/* The effective arguments for txn_body_lock() below. */  struct lock_args  {    svn_lock_t **lock_p; @@ -80,9 +82,19 @@ struct lock_args    svn_boolean_t steal_lock;    apr_time_t expiration_date;    svn_revnum_t current_rev; +  apr_pool_t *result_pool;  }; +/* The body of svn_fs_base__lock(), which see. + +   BATON is a 'struct lock_args *' holding the effective arguments. +   BATON->path is the canonical abspath to lock.  Set *BATON->lock_p +   to the resulting lock.  For the other arguments, see +   svn_fs_lock_many(). + +   This implements the svn_fs_base__retry_txn() 'body' callback type. + */  static svn_error_t *  txn_body_lock(void *baton, trail_t *trail)  { @@ -91,6 +103,8 @@ txn_body_lock(void *baton, trail_t *trail)    svn_lock_t *existing_lock;    svn_lock_t *lock; +  *args->lock_p = NULL; +    SVN_ERR(svn_fs_base__get_path_kind(&kind, args->path, trail, trail->pool));    /* Until we implement directory locks someday, we only allow locks @@ -194,15 +208,15 @@ txn_body_lock(void *baton, trail_t *trail)      }    /* Create a new lock, and add it to the tables. */ -  lock = svn_lock_create(trail->pool); +  lock = svn_lock_create(args->result_pool);    if (args->token) -    lock->token = apr_pstrdup(trail->pool, args->token); +    lock->token = apr_pstrdup(args->result_pool, args->token);    else      SVN_ERR(svn_fs_base__generate_lock_token(&(lock->token), trail->fs, -                                             trail->pool)); -  lock->path = apr_pstrdup(trail->pool, args->path); -  lock->owner = apr_pstrdup(trail->pool, trail->fs->access_ctx->username); -  lock->comment = apr_pstrdup(trail->pool, args->comment); +                                             args->result_pool)); +  lock->path = args->path; /* Already in result_pool. */ +  lock->owner = apr_pstrdup(args->result_pool, trail->fs->access_ctx->username); +  lock->comment = apr_pstrdup(args->result_pool, args->comment);    lock->is_dav_comment = args->is_dav_comment;    lock->creation_date = apr_time_now();    lock->expiration_date = args->expiration_date; @@ -215,31 +229,62 @@ txn_body_lock(void *baton, trail_t *trail)  svn_error_t * -svn_fs_base__lock(svn_lock_t **lock, -                  svn_fs_t *fs, -                  const char *path, -                  const char *token, +svn_fs_base__lock(svn_fs_t *fs, +                  apr_hash_t *targets,                    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_fs_lock_callback_t lock_callback, +                  void *lock_baton, +                  apr_pool_t *result_pool, +                  apr_pool_t *scratch_pool)  { -  struct lock_args args; +  apr_hash_index_t *hi; +  svn_error_t *cb_err = SVN_NO_ERROR; +  svn_revnum_t youngest_rev = SVN_INVALID_REVNUM; +  apr_pool_t *iterpool = svn_pool_create(scratch_pool);    SVN_ERR(svn_fs__check_fs(fs, TRUE)); +  SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, scratch_pool)); -  args.lock_p = lock; -  args.path = svn_fs__canonicalize_abspath(path, pool); -  args.token = token; -  args.comment = comment; -  args.is_dav_comment = is_dav_comment; -  args.steal_lock = steal_lock; -  args.expiration_date = expiration_date; -  args.current_rev = current_rev; - -  return svn_fs_base__retry_txn(fs, txn_body_lock, &args, FALSE, pool); +  for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) +    { +      struct lock_args args; +      const char *path = apr_hash_this_key(hi); +      const svn_fs_lock_target_t *target = apr_hash_this_val(hi); +      svn_lock_t *lock; +      svn_error_t *err = NULL; + +      svn_pool_clear(iterpool); +      args.lock_p = &lock; +      args.path = svn_fs__canonicalize_abspath(path, result_pool); +      args.token = target->token; +      args.comment = comment; +      args.is_dav_comment = is_dav_comment; +      args.steal_lock = steal_lock; +      args.expiration_date = expiration_date; +      args.current_rev = target->current_rev; +      args.result_pool = result_pool; + +      if (SVN_IS_VALID_REVNUM(target->current_rev)) +        { +          if (target->current_rev > youngest_rev) +            err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, +                                    _("No such revision %ld"), +                                    target->current_rev); +        } + +      if (!err) +        err = svn_fs_base__retry_txn(fs, txn_body_lock, &args, TRUE, +                                     iterpool); +      if (!cb_err && lock_callback) +        cb_err = lock_callback(lock_baton, args.path, lock, err, iterpool); +      svn_error_clear(err); +    } +  svn_pool_destroy(iterpool); + +  return svn_error_trace(cb_err);  } @@ -253,11 +298,12 @@ svn_fs_base__generate_lock_token(const char **token,       generate a URI that matches the DAV RFC.  We could change this to       some other URI scheme someday, if we wish. */    *token = apr_pstrcat(pool, "opaquelocktoken:", -                       svn_uuid_generate(pool), (char *)NULL); +                       svn_uuid_generate(pool), SVN_VA_NULL);    return SVN_NO_ERROR;  } +/* The effective arguments for txn_body_unlock() below. */  struct unlock_args  {    const char *path; @@ -266,6 +312,14 @@ struct unlock_args  }; +/* The body of svn_fs_base__unlock(), which see. + +   BATON is a 'struct unlock_args *' holding the effective arguments. +   BATON->path is the canonical path and BATON->token is the token. +   For the other arguments, see svn_fs_unlock_many(). + +   This implements the svn_fs_base__retry_txn() 'body' callback type. + */  static svn_error_t *  txn_body_unlock(void *baton, trail_t *trail)  { @@ -308,19 +362,40 @@ txn_body_unlock(void *baton, trail_t *trail)  svn_error_t *  svn_fs_base__unlock(svn_fs_t *fs, -                    const char *path, -                    const char *token, +                    apr_hash_t *targets,                      svn_boolean_t break_lock, -                    apr_pool_t *pool) +                    svn_fs_lock_callback_t lock_callback, +                    void *lock_baton, +                    apr_pool_t *result_pool, +                    apr_pool_t *scratch_pool)  { -  struct unlock_args args; +  apr_hash_index_t *hi; +  svn_error_t *cb_err = SVN_NO_ERROR; +  apr_pool_t *iterpool = svn_pool_create(scratch_pool);    SVN_ERR(svn_fs__check_fs(fs, TRUE)); -  args.path = svn_fs__canonicalize_abspath(path, pool); -  args.token = token; -  args.break_lock = break_lock; -  return svn_fs_base__retry_txn(fs, txn_body_unlock, &args, TRUE, pool); +  for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi)) +    { +      struct unlock_args args; +      const char *path = apr_hash_this_key(hi); +      const char *token = apr_hash_this_val(hi); +      svn_error_t *err; + +      svn_pool_clear(iterpool); +      args.path = svn_fs__canonicalize_abspath(path, result_pool); +      args.token = token; +      args.break_lock = break_lock; + +      err = svn_fs_base__retry_txn(fs, txn_body_unlock, &args, TRUE, +                                   iterpool); +      if (!cb_err && lock_callback) +        cb_err = lock_callback(lock_baton, path, NULL, err, iterpool); +      svn_error_clear(err); +    } +  svn_pool_destroy(iterpool); + +  return svn_error_trace(cb_err);  } @@ -465,8 +540,9 @@ svn_fs_base__get_locks(svn_fs_t *fs,    args.path = svn_fs__canonicalize_abspath(path, pool);    args.depth = depth;    /* Enough for 100+ locks if the comments are small. */ -  args.stream = svn_stream__from_spillbuf(4 * 1024  /* blocksize */, -                                          64 * 1024 /* maxsize */, +  args.stream = svn_stream__from_spillbuf(svn_spillbuf__create(4 * 1024  /* blocksize */, +                                                               64 * 1024 /* maxsize */, +                                                               pool),                                            pool);    SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_locks, &args, FALSE, pool)); @@ -495,12 +571,12 @@ svn_fs_base__get_locks(svn_fs_t *fs,        /* Now read that much into a buffer. */        skel_buf = apr_palloc(pool, skel_len + 1); -      SVN_ERR(svn_stream_read(stream, skel_buf, &skel_len)); +      SVN_ERR(svn_stream_read_full(stream, skel_buf, &skel_len));        skel_buf[skel_len] = '\0';        /* Read the extra newline that follows the skel. */        len = 1; -      SVN_ERR(svn_stream_read(stream, &c, &len)); +      SVN_ERR(svn_stream_read_full(stream, &c, &len));        if (c != '\n')          return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL); diff --git a/subversion/libsvn_fs_base/lock.h b/subversion/libsvn_fs_base/lock.h index 603e78c5a0f5..3a17ce05ea41 100644 --- a/subversion/libsvn_fs_base/lock.h +++ b/subversion/libsvn_fs_base/lock.h @@ -33,34 +33,41 @@ extern "C" {  /* These functions implement part of the FS loader library's fs -   vtables.  See the public svn_fs.h for docstrings.*/ +   vtables. */ -svn_error_t *svn_fs_base__lock(svn_lock_t **lock, -                               svn_fs_t *fs, -                               const char *path, -                               const char *token, +/* See svn_fs_lock(), svn_fs_lock_many(). */ +svn_error_t *svn_fs_base__lock(svn_fs_t *fs, +                               apr_hash_t *targets,                                 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_fs_lock_callback_t lock_callback, +                               void *lock_baton, +                               apr_pool_t *result_pool, +                               apr_pool_t *scratch_pool); +/* See svn_fs_generate_lock_token(). */  svn_error_t *svn_fs_base__generate_lock_token(const char **token,                                                svn_fs_t *fs,                                                apr_pool_t *pool); +/* See svn_fs_unlock(), svn_fs_unlock_many(). */  svn_error_t *svn_fs_base__unlock(svn_fs_t *fs, -                                 const char *path, -                                 const char *token, +                                 apr_hash_t *targets,                                   svn_boolean_t break_lock, -                                 apr_pool_t *pool); +                                 svn_fs_lock_callback_t lock_callback, +                                 void *lock_baton, +                                 apr_pool_t *result_pool, +                                 apr_pool_t *scratch_pool); +/* See svn_fs_get_lock(). */  svn_error_t *svn_fs_base__get_lock(svn_lock_t **lock,                                     svn_fs_t *fs,                                     const char *path,                                     apr_pool_t *pool); +/* See svn_fs_get_locks2(). */  svn_error_t *  svn_fs_base__get_locks(svn_fs_t *fs,                         const char *path, diff --git a/subversion/libsvn_fs_base/reps-strings.c b/subversion/libsvn_fs_base/reps-strings.c index 553075d4eaef..e88583aa4697 100644 --- a/subversion/libsvn_fs_base/reps-strings.c +++ b/subversion/libsvn_fs_base/reps-strings.c @@ -920,7 +920,7 @@ txn_body_read_rep(void *baton, trail_t *trail)                                             args->rb->md5_checksum)))                  return svn_error_create(SVN_ERR_FS_CORRUPT,                          svn_checksum_mismatch_err(rep->md5_checksum, -                             args->rb->sha1_checksum, trail->pool, +                             args->rb->md5_checksum, trail->pool,                               _("MD5 checksum mismatch on representation '%s'"),                               args->rb->rep_key),                          NULL); @@ -1224,7 +1224,8 @@ svn_fs_base__rep_contents_read_stream(svn_stream_t **rs_p,    SVN_ERR(rep_read_get_baton(&rb, fs, rep_key, use_trail_for_reads,                               trail, pool));    *rs_p = svn_stream_create(rb, pool); -  svn_stream_set_read(*rs_p, rep_read_contents); +  svn_stream_set_read2(*rs_p, NULL /* only full read support */, +                       rep_read_contents);    return SVN_NO_ERROR;  } diff --git a/subversion/libsvn_fs_base/revs-txns.c b/subversion/libsvn_fs_base/revs-txns.c index d21884311a83..f1029f4e0d0f 100644 --- a/subversion/libsvn_fs_base/revs-txns.c +++ b/subversion/libsvn_fs_base/revs-txns.c @@ -574,6 +574,10 @@ svn_fs_base__set_txn_prop(svn_fs_t *fs,      txn->proplist = apr_hash_make(pool);    /* Set the property. */ +  if (svn_hash_gets(txn->proplist, SVN_FS__PROP_TXN_CLIENT_DATE) +      && !strcmp(name, SVN_PROP_REVISION_DATE)) +    svn_hash_sets(txn->proplist, SVN_FS__PROP_TXN_CLIENT_DATE, +                  svn_string_create("1", pool));    svn_hash_sets(txn->proplist, name, value);    /* Now overwrite the transaction. */ @@ -707,6 +711,34 @@ txn_body_begin_txn(void *baton, trail_t *trail)        SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));      } +  /* Put a datestamp on the newly created txn, so we always know +     exactly how old it is.  (This will help sysadmins identify +     long-abandoned txns that may need to be manually removed.) Do +     this before setting CLIENT_DATE so that it is not recorded as an +     explicit setting. */ +  { +    struct change_txn_prop_args cpargs; +    svn_string_t date; +    cpargs.fs = trail->fs; +    cpargs.id = txn_id; +    cpargs.name = SVN_PROP_REVISION_DATE; +    date.data  = svn_time_to_cstring(apr_time_now(), trail->pool); +    date.len = strlen(date.data); +    cpargs.value = &date; +    SVN_ERR(txn_body_change_txn_prop(&cpargs, trail)); +  } + +  if (args->flags & SVN_FS_TXN_CLIENT_DATE) +    { +      struct change_txn_prop_args cpargs; +      cpargs.fs = trail->fs; +      cpargs.id = txn_id; +      cpargs.name = SVN_FS__PROP_TXN_CLIENT_DATE; +      cpargs.value = svn_string_create("0", trail->pool); + +      SVN_ERR(txn_body_change_txn_prop(&cpargs, trail)); +    } +    *args->txn_p = make_txn(trail->fs, txn_id, args->base_rev, trail->pool);    return SVN_NO_ERROR;  } @@ -722,7 +754,6 @@ svn_fs_base__begin_txn(svn_fs_txn_t **txn_p,  {    svn_fs_txn_t *txn;    struct begin_txn_args args; -  svn_string_t date;    SVN_ERR(svn_fs__check_fs(fs, TRUE)); @@ -733,15 +764,7 @@ svn_fs_base__begin_txn(svn_fs_txn_t **txn_p,    *txn_p = txn; -  /* Put a datestamp on the newly created txn, so we always know -     exactly how old it is.  (This will help sysadmins identify -     long-abandoned txns that may need to be manually removed.)  When -     a txn is promoted to a revision, this property will be -     automatically overwritten with a revision datestamp. */ -  date.data = svn_time_to_cstring(apr_time_now(), pool); -  date.len = strlen(date.data); -  return svn_fs_base__change_txn_prop(txn, SVN_PROP_REVISION_DATE, -                                       &date, pool); +  return SVN_NO_ERROR;  } @@ -897,7 +920,7 @@ delete_txn_tree(svn_fs_t *fs,    svn_error_t *err;    /* If this sucker isn't mutable, there's nothing to do. */ -  if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(id), txn_id) != 0) +  if (strcmp(svn_fs_base__id_txn_id(id), txn_id) != 0)      return SVN_NO_ERROR;    /* See if the thing has dirents that need to be recursed upon.  If diff --git a/subversion/libsvn_fs_base/tree.c b/subversion/libsvn_fs_base/tree.c index 046ab5daa072..924e7c94a2a0 100644 --- a/subversion/libsvn_fs_base/tree.c +++ b/subversion/libsvn_fs_base/tree.c @@ -543,7 +543,7 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p,    parent_copy_id = svn_fs_base__id_copy_id(parent_id);    /* Easy out: if this child is already mutable, we have nothing to do. */ -  if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(child_id), txn_id) == 0) +  if (strcmp(svn_fs_base__id_txn_id(child_id), txn_id) == 0)      return SVN_NO_ERROR;    /* If the child and its parent are on the same branch, then the @@ -560,7 +560,7 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p,       target of any copy, and therefore must be on the same branch as       its parent.  */    if ((strcmp(child_copy_id, "0") == 0) -      || (svn_fs_base__key_compare(child_copy_id, parent_copy_id) == 0)) +      || (strcmp(child_copy_id, parent_copy_id) == 0))      {        *inherit_p = copy_id_inherit_parent;        return SVN_NO_ERROR; @@ -569,7 +569,8 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p,      {        copy_t *copy;        SVN_ERR(svn_fs_bdb__get_copy(©, fs, child_copy_id, trail, pool)); -      if (svn_fs_base__id_compare(copy->dst_noderev_id, child_id) == -1) +      if (   svn_fs_base__id_compare(copy->dst_noderev_id, child_id) +          == svn_fs_node_unrelated)          {            *inherit_p = copy_id_inherit_parent;            return SVN_NO_ERROR; @@ -1027,6 +1028,30 @@ base_node_id(const svn_fs_id_t **id_p,    return SVN_NO_ERROR;  } +static svn_error_t * +base_node_relation(svn_fs_node_relation_t *relation, +                   svn_fs_root_t *root_a, const char *path_a, +                   svn_fs_root_t *root_b, const char *path_b, +                   apr_pool_t *pool) +{ +  const svn_fs_id_t *id_a, *id_b; + +  /* Paths from different repository are never related. */ +  if (root_a->fs != root_b->fs) +    { +      *relation = svn_fs_node_unrelated; +      return SVN_NO_ERROR; +    } + +  /* Naive implementation. */ +  SVN_ERR(base_node_id(&id_a, root_a, path_a, pool)); +  SVN_ERR(base_node_id(&id_b, root_b, path_b, pool)); + +  *relation = svn_fs_base__id_compare(id_a, id_b); + +  return SVN_NO_ERROR; +} +  struct node_created_rev_args {    svn_revnum_t revision; @@ -1212,7 +1237,7 @@ base_node_prop(svn_string_t **value_p,    args.propname = propname;    SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args,                                   FALSE, scratch_pool)); -  *value_p = value ? svn_string_dup(value, pool) : NULL; +  *value_p = svn_string_dup(value, pool);    svn_pool_destroy(scratch_pool);    return SVN_NO_ERROR;  } @@ -1260,6 +1285,21 @@ base_node_proplist(apr_hash_t **table_p,    return SVN_NO_ERROR;  } +static svn_error_t * +base_node_has_props(svn_boolean_t *has_props, +                    svn_fs_root_t *root, +                    const char *path, +                    apr_pool_t *scratch_pool) +{ +  apr_hash_t *props; + +  SVN_ERR(base_node_proplist(&props, root, path, scratch_pool)); + +  *has_props = (0 < apr_hash_count(props)); + +  return SVN_NO_ERROR; +} +  struct change_node_prop_args {    svn_fs_root_t *root; @@ -1369,6 +1409,7 @@ struct things_changed_args    svn_fs_root_t *root2;    const char *path1;    const char *path2; +  svn_boolean_t strict;    apr_pool_t *pool;  }; @@ -1378,11 +1419,26 @@ txn_body_props_changed(void *baton, trail_t *trail)  {    struct things_changed_args *args = baton;    dag_node_t *node1, *node2; +  apr_hash_t *proplist1, *proplist2;    SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));    SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool)); -  return svn_fs_base__things_different(args->changed_p, NULL, -                                       node1, node2, trail, trail->pool); +  SVN_ERR(svn_fs_base__things_different(args->changed_p, NULL, +                                        node1, node2, trail, trail->pool)); + +  /* Is there a potential false positive and do we want to correct it? */ +  if (!args->strict || !*args->changed_p) +    return SVN_NO_ERROR; + +  /* Different representations. They might still have equal contents. */ +  SVN_ERR(svn_fs_base__dag_get_proplist(&proplist1, node1, +                                        trail, trail->pool)); +  SVN_ERR(svn_fs_base__dag_get_proplist(&proplist2, node2, +                                        trail, trail->pool)); + +  *args->changed_p = !svn_fs__prop_lists_equal(proplist1, proplist2, +                                               trail->pool); +  return SVN_NO_ERROR;  } @@ -1392,6 +1448,7 @@ base_props_changed(svn_boolean_t *changed_p,                     const char *path1,                     svn_fs_root_t *root2,                     const char *path2, +                   svn_boolean_t strict,                     apr_pool_t *pool)  {    struct things_changed_args args; @@ -1408,6 +1465,7 @@ base_props_changed(svn_boolean_t *changed_p,    args.path2      = path2;    args.changed_p  = changed_p;    args.pool       = pool; +  args.strict     = strict;    return svn_fs_base__retry_txn(root1->fs, txn_body_props_changed, &args,                                  TRUE, pool); @@ -1561,6 +1619,25 @@ base_dir_entries(apr_hash_t **table_p,    return SVN_NO_ERROR;  } +static svn_error_t * +base_dir_optimal_order(apr_array_header_t **ordered_p, +                       svn_fs_root_t *root, +                       apr_hash_t *entries, +                       apr_pool_t *result_pool, +                       apr_pool_t *scratch_pool) +{ +  /* 1:1 copy of entries with no differnce in ordering */ +  apr_hash_index_t *hi; +  apr_array_header_t *result +    = apr_array_make(result_pool, apr_hash_count(entries), +                     sizeof(svn_fs_dirent_t *)); +  for (hi = apr_hash_first(scratch_pool, entries); hi; hi = apr_hash_next(hi)) +    APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = apr_hash_this_val(hi); + +  *ordered_p = result; +  return SVN_NO_ERROR; +} +  /* Merges and commits. */ @@ -3104,7 +3181,8 @@ txn_body_copy(void *baton,    if ((to_parent_path->node)        && (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node),                                    svn_fs_base__dag_get_id -                                  (to_parent_path->node)) == 0)) +                                  (to_parent_path->node)) +          == svn_fs_node_unchanged))      return SVN_NO_ERROR;    if (! from_root->is_txn_root) @@ -3292,8 +3370,8 @@ txn_body_copied_from(void *baton, trail_t *trail)      return SVN_NO_ERROR;    /* If NODE's copy-ID is the same as that of its predecessor... */ -  if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id), -                               svn_fs_base__id_copy_id(pred_id)) != 0) +  if (strcmp(svn_fs_base__id_copy_id(node_id), +             svn_fs_base__id_copy_id(pred_id)) != 0)      {        /* ... then NODE was either the target of a copy operation,           a copied subtree item.  We examine the actual copy record @@ -3942,11 +4020,53 @@ txn_body_contents_changed(void *baton, trail_t *trail)  {    struct things_changed_args *args = baton;    dag_node_t *node1, *node2; +  svn_checksum_t *checksum1, *checksum2; +  svn_stream_t *stream1, *stream2; +  svn_boolean_t same;    SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));    SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool)); -  return svn_fs_base__things_different(NULL, args->changed_p, -                                       node1, node2, trail, trail->pool); +  SVN_ERR(svn_fs_base__things_different(NULL, args->changed_p, +                                        node1, node2, trail, trail->pool)); + +  /* Is there a potential false positive and do we want to correct it? */ +  if (!args->strict || !*args->changed_p) +    return SVN_NO_ERROR; + +  /* Different representations. They might still have equal contents. */ + +  /* Compare MD5 checksums.  These should be readily accessible. */ +  SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_md5, +                                         node1, trail, trail->pool)); +  SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_md5, +                                         node2, trail, trail->pool)); + +  /* Different MD5 checksums -> different contents */ +  if (!svn_checksum_match(checksum1, checksum2)) +    return SVN_NO_ERROR; + +  /* Paranoia. Compare SHA1 checksums because that's the level of +     confidence we require for e.g. the working copy. */ +  SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_sha1, +                                         node1, trail, trail->pool)); +  SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_sha1, +                                         node2, trail, trail->pool)); + +  /* Different SHA1 checksums -> different contents */ +  if (checksum1 && checksum2) +    { +      *args->changed_p = !svn_checksum_match(checksum1, checksum2); +      return SVN_NO_ERROR; +    } + +  /* SHA1 checksums are not available for very old reps / repositories. */ +  SVN_ERR(svn_fs_base__dag_get_contents(&stream1, node1, trail, trail->pool)); +  SVN_ERR(svn_fs_base__dag_get_contents(&stream2, node2, trail, trail->pool)); +  SVN_ERR(svn_stream_contents_same2(&same, stream1, stream2, trail->pool)); + +  /* Now, it's definitive. */ +  *args->changed_p = !same; +  return SVN_NO_ERROR;  } @@ -3958,6 +4078,7 @@ base_contents_changed(svn_boolean_t *changed_p,                        const char *path1,                        svn_fs_root_t *root2,                        const char *path2, +                      svn_boolean_t strict,                        apr_pool_t *pool)  {    struct things_changed_args args; @@ -3989,6 +4110,7 @@ base_contents_changed(svn_boolean_t *changed_p,    args.path2      = path2;    args.changed_p  = changed_p;    args.pool       = pool; +  args.strict     = strict;    return svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed, &args,                                  TRUE, pool); @@ -4108,7 +4230,8 @@ static svn_error_t *  base_node_history(svn_fs_history_t **history_p,                    svn_fs_root_t *root,                    const char *path, -                  apr_pool_t *pool) +                  apr_pool_t *result_pool, +                  apr_pool_t *scratch_pool)  {    svn_node_kind_t kind; @@ -4117,15 +4240,16 @@ base_node_history(svn_fs_history_t **history_p,      return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);    /* And we require that the path exist in the root. */ -  SVN_ERR(base_check_path(&kind, root, path, pool)); +  SVN_ERR(base_check_path(&kind, root, path, scratch_pool));    if (kind == svn_node_none)      return SVN_FS__NOT_FOUND(root, path);    /* Okay, all seems well.  Build our history object and return it. */    *history_p = assemble_history(root->fs, -                                svn_fs__canonicalize_abspath(path, pool), +                                svn_fs__canonicalize_abspath(path, +                                                             result_pool),                                  root->rev, FALSE, NULL, -                                SVN_INVALID_REVNUM, pool); +                                SVN_INVALID_REVNUM, result_pool);    return SVN_NO_ERROR;  } @@ -4298,8 +4422,7 @@ txn_body_history_prev(void *baton, trail_t *trail)       (which is either a real predecessor, or is the node itself       playing the predecessor role to an imaginary mutable successor),       then we need to report a copy.  */ -  if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id), -                               end_copy_id) != 0) +  if (strcmp(svn_fs_base__id_copy_id(node_id), end_copy_id) != 0)      {        const char *remainder;        dag_node_t *dst_node; @@ -4376,7 +4499,8 @@ static svn_error_t *  base_history_prev(svn_fs_history_t **prev_history_p,                    svn_fs_history_t *history,                    svn_boolean_t cross_copies, -                  apr_pool_t *pool) +                  apr_pool_t *result_pool, +                  apr_pool_t *scratch_pool)  {    svn_fs_history_t *prev_history = NULL;    base_history_data_t *bhd = history->fsap_data; @@ -4390,10 +4514,12 @@ base_history_prev(svn_fs_history_t **prev_history_p,      {        if (! bhd->is_interesting)          prev_history = assemble_history(fs, "/", bhd->revision, -                                        1, NULL, SVN_INVALID_REVNUM, pool); +                                        1, NULL, SVN_INVALID_REVNUM, +                                        result_pool);        else if (bhd->revision > 0)          prev_history = assemble_history(fs, "/", bhd->revision - 1, -                                        1, NULL, SVN_INVALID_REVNUM, pool); +                                        1, NULL, SVN_INVALID_REVNUM, +                                        result_pool);      }    else      { @@ -4407,9 +4533,9 @@ base_history_prev(svn_fs_history_t **prev_history_p,            args.prev_history_p = &prev_history;            args.history = prev_history;            args.cross_copies = cross_copies; -          args.pool = pool; +          args.pool = result_pool;            SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args, -                                         FALSE, pool)); +                                         FALSE, result_pool));            if (! prev_history)              break;            bhd = prev_history->fsap_data; @@ -5370,20 +5496,23 @@ static root_vtable_t root_vtable = {    base_check_path,    base_node_history,    base_node_id, +  base_node_relation,    base_node_created_rev,    base_node_origin_rev,    base_node_created_path,    base_delete_node, +  base_copy, +  base_revision_link,    base_copied_from,    base_closest_copy,    base_node_prop,    base_node_proplist, +  base_node_has_props,    base_change_node_prop,    base_props_changed,    base_dir_entries, +  base_dir_optimal_order,    base_make_dir, -  base_copy, -  base_revision_link,    base_file_length,    base_file_checksum,    base_file_contents, | 
