Exemplo n.º 1
0
svn_error_t  *
svn_repos__hooks_pre_revprop_change(svn_repos_t *repos,
                                    svn_revnum_t rev,
                                    const char *author,
                                    const char *name,
                                    const svn_string_t *new_value,
                                    char action,
                                    apr_pool_t *pool)
{
  const char *hook = svn_repos_pre_revprop_change_hook(repos, pool);
  svn_boolean_t broken_link;

  if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
    {
      return hook_symlink_error(hook);
    }
  else if (hook)
    {
      const char *args[7];
      apr_file_t *stdin_handle = NULL;
      char action_string[2];

      /* Pass the new value as stdin to hook */
      if (new_value)
        SVN_ERR(create_temp_file(&stdin_handle, new_value, pool));
      else
        SVN_ERR(svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
                                 APR_READ, APR_OS_DEFAULT, pool));

      action_string[0] = action;
      action_string[1] = '\0';

      args[0] = hook;
      args[1] = svn_dirent_local_style(svn_repos_path(repos, pool), pool);
      args[2] = apr_psprintf(pool, "%ld", rev);
      args[3] = author ? author : "";
      args[4] = name;
      args[5] = action_string;
      args[6] = NULL;

      SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_PRE_REVPROP_CHANGE, hook,
                           args, repos->hooks_env, stdin_handle, pool));

      SVN_ERR(svn_io_file_close(stdin_handle, pool));
    }
  else
    {
      /* If the pre- hook doesn't exist at all, then default to
         MASSIVE PARANOIA.  Changing revision properties is a lossy
         operation; so unless the repository admininstrator has
         *deliberately* created the pre-hook, disallow all changes. */
      return
        svn_error_create
        (SVN_ERR_REPOS_DISABLED_FEATURE, NULL,
         _("Repository has not been enabled to accept revision propchanges;\n"
           "ask the administrator to create a pre-revprop-change hook"));
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 2
0
static svn_error_t *
close_handler_apr(void *baton)
{
  struct baton_apr *btn = baton;

  return svn_error_trace(svn_io_file_close(btn->file, btn->pool));
}
Exemplo n.º 3
0
svn_error_t *
svn_diff_close_patch_file(svn_patch_file_t *patch_file,
                          apr_pool_t *scratch_pool)
{
  return svn_error_trace(svn_io_file_close(patch_file->apr_file,
                                           scratch_pool));
}
Exemplo n.º 4
0
svn_error_t *
svn_wc__db_util_open_db(svn_sqlite__db_t **sdb,
                        const char *dir_abspath,
                        const char *sdb_fname,
                        svn_sqlite__mode_t smode,
                        svn_boolean_t exclusive,
                        const char *const *my_statements,
                        apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool)
{
  const char *sdb_abspath = svn_wc__adm_child(dir_abspath, sdb_fname,
                                              scratch_pool);

  if (smode != svn_sqlite__mode_rwcreate)
    {
      svn_node_kind_t kind;

      /* A file stat is much cheaper then a failed database open handled
         by SQLite. */
      SVN_ERR(svn_io_check_path(sdb_abspath, &kind, scratch_pool));

      if (kind != svn_node_file)
        return svn_error_createf(APR_ENOENT, NULL,
                                 _("Working copy database '%s' not found"),
                                 svn_dirent_local_style(sdb_abspath,
                                                        scratch_pool));
    }
#ifndef WIN32
  else
    {
      apr_file_t *f;

      /* A standard SQLite build creates a DB with mode 644 ^ !umask
         which means the file doesn't have group/world write access
         even when umask allows it. By ensuring the file exists before
         SQLite gets involved we give it the permissions allowed by
         umask. */
      SVN_ERR(svn_io_file_open(&f, sdb_abspath,
                               (APR_READ | APR_WRITE | APR_CREATE),
                               APR_OS_DEFAULT, scratch_pool));
      SVN_ERR(svn_io_file_close(f, scratch_pool));
    }
#endif

  SVN_ERR(svn_sqlite__open(sdb, sdb_abspath, smode,
                           my_statements ? my_statements : statements,
                           0, NULL, result_pool, scratch_pool));

  if (exclusive)
    SVN_ERR(svn_sqlite__exec_statements(*sdb, STMT_PRAGMA_LOCKING_MODE));

  SVN_ERR(svn_sqlite__create_scalar_function(*sdb, "relpath_depth", 1,
                                             relpath_depth_sqlite, NULL));

  return SVN_NO_ERROR;
}
Exemplo n.º 5
0
svn_error_t  *
svn_repos__hooks_post_revprop_change(svn_repos_t *repos,
                                     apr_hash_t *hooks_env,
                                     svn_revnum_t rev,
                                     const char *author,
                                     const char *name,
                                     const svn_string_t *old_value,
                                     char action,
                                     apr_pool_t *pool)
{
  const char *hook = svn_repos_post_revprop_change_hook(repos, pool);
  svn_boolean_t broken_link;

  if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
    {
      return hook_symlink_error(hook);
    }
  else if (hook)
    {
      const char *args[7];
      apr_file_t *stdin_handle = NULL;
      char action_string[2];

      /* Pass the old value as stdin to hook */
      if (old_value)
        SVN_ERR(create_temp_file(&stdin_handle, old_value, pool));
      else
        SVN_ERR(svn_io_file_open(&stdin_handle, SVN_NULL_DEVICE_NAME,
                                 APR_READ, APR_OS_DEFAULT, pool));

      action_string[0] = action;
      action_string[1] = '\0';

      args[0] = hook;
      args[1] = svn_dirent_local_style(svn_repos_path(repos, pool), pool);
      args[2] = apr_psprintf(pool, "%ld", rev);
      args[3] = author ? author : "";
      args[4] = name;
      args[5] = action_string;
      args[6] = NULL;

      SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_POST_REVPROP_CHANGE, hook,
                           args, hooks_env, stdin_handle, pool));

      SVN_ERR(svn_io_file_close(stdin_handle, pool));
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 6
0
static svn_error_t *
write_fs_type(const char *path, const char *fs_type, apr_pool_t *pool)
{
  const char *filename;
  apr_file_t *file;

  filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
  SVN_ERR(svn_io_file_open(&file, filename,
                           APR_WRITE|APR_CREATE|APR_TRUNCATE|APR_BUFFERED,
                           APR_OS_DEFAULT, pool));
  SVN_ERR(svn_io_file_write_full(file, fs_type, strlen(fs_type), NULL,
                                 pool));
  SVN_ERR(svn_io_file_write_full(file, "\n", 1, NULL, pool));
  return svn_error_trace(svn_io_file_close(file, pool));
}
Exemplo n.º 7
0
svn_error_t *
svn_fs_x__close_revision_file(svn_fs_x__revision_file_t *file)
{
  if (file->stream)
    SVN_ERR(svn_stream_close(file->stream));
  if (file->file)
    SVN_ERR(svn_io_file_close(file->file, file->pool));

  file->file = NULL;
  file->stream = NULL;
  file->l2p_stream = NULL;
  file->p2l_stream = NULL;

  return SVN_NO_ERROR;
}
Exemplo n.º 8
0
/* Write the PID of the current process as a decimal number, followed by a
   newline to the file FILENAME, using POOL for temporary allocations. */
static svn_error_t *write_pid_file(const char *filename, apr_pool_t *pool)
{
  apr_file_t *file;
  const char *contents = apr_psprintf(pool, "%" APR_PID_T_FMT "\n",
                                             getpid());

  SVN_ERR(svn_io_remove_file2(filename, TRUE, pool));
  SVN_ERR(svn_io_file_open(&file, filename,
                           APR_WRITE | APR_CREATE | APR_EXCL,
                           APR_OS_DEFAULT, pool));
  SVN_ERR(svn_io_file_write_full(file, contents, strlen(contents), NULL,
                                 pool));

  SVN_ERR(svn_io_file_close(file, pool));

  return SVN_NO_ERROR;
}
Exemplo n.º 9
0
svn_error_t *
svn_fs_type(const char **fs_type, const char *path, apr_pool_t *pool)
{
  const char *filename;
  char buf[128];
  svn_error_t *err;
  apr_file_t *file;
  apr_size_t len;

  /* Read the fsap-name file to get the FSAP name, or assume the (old)
     default.  For old repositories I suppose we could check some
     other file, DB_CONFIG or strings say, but for now just check the
     directory exists. */
  filename = svn_dirent_join(path, FS_TYPE_FILENAME, pool);
  err = svn_io_file_open(&file, filename, APR_READ|APR_BUFFERED, 0, pool);
  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
    {
      svn_node_kind_t kind;
      svn_error_t *err2 = svn_io_check_path(path, &kind, pool);
      if (err2)
        {
          svn_error_clear(err2);
          return err;
        }
      if (kind == svn_node_dir)
        {
          svn_error_clear(err);
          *fs_type = SVN_FS_TYPE_BDB;
          return SVN_NO_ERROR;
        }
      return err;
    }
  else if (err)
    return err;

  len = sizeof(buf);
  SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
  SVN_ERR(svn_io_file_close(file, pool));
  *fs_type = apr_pstrdup(pool, buf);

  return SVN_NO_ERROR;
}
Exemplo n.º 10
0
svn_error_t  *
svn_repos__hooks_post_unlock(svn_repos_t *repos,
                             apr_hash_t *hooks_env,
                             const apr_array_header_t *paths,
                             const char *username,
                             apr_pool_t *pool)
{
  const char *hook = svn_repos_post_unlock_hook(repos, pool);
  svn_boolean_t broken_link;

  if ((hook = check_hook_cmd(hook, &broken_link, pool)) && broken_link)
    {
      return hook_symlink_error(hook);
    }
  else if (hook)
    {
      const char *args[5];
      apr_file_t *stdin_handle = NULL;
      svn_string_t *paths_str = svn_string_create(svn_cstring_join
                                                  (paths, "\n", pool),
                                                  pool);

      SVN_ERR(create_temp_file(&stdin_handle, paths_str, pool));

      args[0] = hook;
      args[1] = svn_dirent_local_style(svn_repos_path(repos, pool), pool);
      args[2] = username ? username : "";
      args[3] = NULL;
      args[4] = NULL;

      SVN_ERR(run_hook_cmd(NULL, SVN_REPOS__HOOK_POST_UNLOCK, hook, args,
                           hooks_env, stdin_handle, pool));

      SVN_ERR(svn_io_file_close(stdin_handle, pool));
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 11
0
svn_error_t *
svn_fs_x__close_revision_file(svn_fs_x__revision_file_t *file)
{
  /* Close sub-objects properly */
  if (file->stream)
    SVN_ERR(svn_stream_close(file->stream));
  if (file->file)
    SVN_ERR(svn_io_file_close(file->file, file->pool));

  /* Release the memory. */
  if (file->pool)
    svn_pool_clear(file->pool);

  /* Reset pointers to objects previously allocated from FILE->POOL. */
  file->file = NULL;
  file->stream = NULL;
  file->l2p_stream = NULL;
  file->p2l_stream = NULL;

  /* Cause any index data getters to re-read the footer. */
  file->l2p_info.start = -1;
  return SVN_NO_ERROR;
}
Exemplo n.º 12
0
svn_error_t *
svn_wc_create_tmp_file2(apr_file_t **fp,
                        const char **new_name,
                        const char *path,
                        svn_io_file_del_t delete_when,
                        apr_pool_t *pool)
{
  const char *temp_dir;
  apr_file_t *file;

  SVN_ERR_ASSERT(fp || new_name);

  temp_dir = svn_wc__adm_child(path, SVN_WC__ADM_TMP, pool);

  SVN_ERR(svn_io_open_unique_file3(&file, new_name, temp_dir,
                                   delete_when, pool, pool));

  if (fp)
    *fp = file;
  else
    SVN_ERR(svn_io_file_close(file, pool));

  return SVN_NO_ERROR;
}
Exemplo n.º 13
0
/* Create a PATCH_FILE containing the contents of DIFF. */
static svn_error_t *
create_patch_file(svn_patch_file_t **patch_file,
                  const char *diff, apr_pool_t *pool)
{
  apr_size_t bytes;
  apr_size_t len;
  const char *path;
  apr_file_t *apr_file;

  /* Create a patch file. */
  SVN_ERR(svn_io_open_unique_file3(&apr_file, &path, NULL,
                                   svn_io_file_del_on_pool_cleanup,
                                   pool, pool));

  bytes = strlen(diff);
  SVN_ERR(svn_io_file_write_full(apr_file, diff, bytes, &len, pool));
  if (len != bytes)
    return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
                             "Cannot write to '%s'", path);
  SVN_ERR(svn_io_file_close(apr_file, pool));
  SVN_ERR(svn_diff_open_patch_file(patch_file, path, pool));

  return SVN_NO_ERROR;
}
Exemplo n.º 14
0
dav_error *
dav_svn__store_activity(const dav_svn_repos *repos,
                        const char *activity_id,
                        const char *txn_name)
{
  const char *final_path, *tmp_path, *activity_contents;
  svn_error_t *err;
  apr_file_t *activity_file;

  /* Create activities directory if it does not yet exist. */
  err = svn_io_make_dir_recursively(repos->activities_db, repos->pool);
  if (err != NULL)
    return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                "could not initialize activity db.",
                                repos->pool);

  final_path = activity_pathname(repos, activity_id);
  err = svn_io_open_unique_file2(&activity_file, &tmp_path, final_path,
                                 ".tmp", svn_io_file_del_none, repos->pool);
  if (err)
    {
      svn_error_t *serr = svn_error_quick_wrap(err, "Can't open activity db");
      return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "could not open files.",
                                  repos->pool);
    }

  activity_contents = apr_psprintf(repos->pool, "%s\n%s\n",
                                   txn_name, activity_id);
  err = svn_io_file_write_full(activity_file, activity_contents,
                               strlen(activity_contents), NULL, repos->pool);
  if (err)
    {
      svn_error_t *serr = svn_error_quick_wrap(err,
                                               "Can't write to activity db");

      /* Try to remove the tmp file, but we already have an error... */
      svn_error_clear(svn_io_file_close(activity_file, repos->pool));
      svn_error_clear(svn_io_remove_file(tmp_path, repos->pool));
      return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                  "could not write files.",
                                  repos->pool);
    }

  err = svn_io_file_close(activity_file, repos->pool);
  if (err)
    {
      svn_error_clear(svn_io_remove_file(tmp_path, repos->pool));
      return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                  "could not close files.",
                                  repos->pool);
    }

  err = svn_io_file_rename(tmp_path, final_path, repos->pool);
  if (err)
    {
      svn_error_clear(svn_io_remove_file(tmp_path, repos->pool));
      return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
                                  "could not replace files.",
                                  repos->pool);
    }

  return NULL;
}
Exemplo n.º 15
0
/* Drive EDITOR to affect the change represented by OPERATION.  HEAD
   is the last-known youngest revision in the repository. */
static svn_error_t *
drive(struct operation *operation,
      svn_revnum_t head,
      const svn_delta_editor_t *editor,
      apr_pool_t *pool)
{
  apr_pool_t *subpool = svn_pool_create(pool);
  apr_hash_index_t *hi;
  struct driver_state state;
  for (hi = apr_hash_first(pool, operation->children);
       hi; hi = apr_hash_next(hi))
    {
      const void *key;
      void *val;
      struct operation *child;
      void *file_baton = NULL;

      svn_pool_clear(subpool);
      apr_hash_this(hi, &key, NULL, &val);
      child = val;

      /* Deletes and replacements are simple -- delete something. */
      if (child->operation == OP_DELETE || child->operation == OP_REPLACE)
        {
          SVN_ERR(editor->delete_entry(key, head, operation->baton, subpool));
        }
      /* Opens could be for directories or files. */
      if (child->operation == OP_OPEN)
        {
          if (child->kind == svn_node_dir)
            {
              SVN_ERR(editor->open_directory(key, operation->baton, head,
                                             subpool, &child->baton));
            }
          else
            {
              SVN_ERR(editor->open_file(key, operation->baton, head,
                                        subpool, &file_baton));
            }
        }
      /* Adds and replacements could also be for directories or files. */
      if (child->operation == OP_ADD || child->operation == OP_REPLACE
          || child->operation == OP_PROPSET)
        {
          if (child->kind == svn_node_dir)
            {
              SVN_ERR(editor->add_directory(key, operation->baton,
                                            child->url, child->rev,
                                            subpool, &child->baton));
            }
          else
            {
              SVN_ERR(editor->add_file(key, operation->baton, child->url,
                                       child->rev, subpool, &file_baton));
            }
        }
      /* If there's a source file and an open file baton, we get to
         change textual contents. */
      if ((child->src_file) && (file_baton))
        {
          svn_txdelta_window_handler_t handler;
          void *handler_baton;
          svn_stream_t *contents;
          apr_file_t *f = NULL;

          SVN_ERR(editor->apply_textdelta(file_baton, NULL, subpool,
                                          &handler, &handler_baton));
          if (strcmp(child->src_file, "-"))
            {
              SVN_ERR(svn_io_file_open(&f, child->src_file, APR_READ,
                                       APR_OS_DEFAULT, pool));
            }
          else
            {
              apr_status_t apr_err = apr_file_open_stdin(&f, pool);
              if (apr_err)
                return svn_error_wrap_apr(apr_err, "Can't open stdin");
            }
          contents = svn_stream_from_aprfile(f, pool);
          SVN_ERR(svn_txdelta_send_stream(contents, handler,
                                          handler_baton, NULL, pool));
          SVN_ERR(svn_io_file_close(f, pool));
        }
      /* If we opened a file, we need to apply outstanding propmods,
         then close it. */
      if (file_baton)
        {
          if ((child->kind == svn_node_file)
              && (! apr_is_empty_table(child->props)))
            {
              state.baton = file_baton;
              state.pool = subpool;
              state.editor = editor;
              state.kind = child->kind;
              if (! apr_table_do(set_props, &state, child->props, NULL))
                SVN_ERR(state.err);
            }
          SVN_ERR(editor->close_file(file_baton, NULL, subpool));
        }
      /* If we opened, added, or replaced a directory, we need to
         recurse, apply outstanding propmods, and then close it. */
      if ((child->kind == svn_node_dir)
          && (child->operation == OP_OPEN
              || child->operation == OP_ADD
              || child->operation == OP_REPLACE))
        {
          SVN_ERR(drive(child, head, editor, subpool));
          if ((child->kind == svn_node_dir)
              && (! apr_is_empty_table(child->props)))
            {
              state.baton = child->baton;
              state.pool = subpool;
              state.editor = editor;
              state.kind = child->kind;
              if (! apr_table_do(set_props, &state, child->props, NULL))
                SVN_ERR(state.err);
            }
          SVN_ERR(editor->close_directory(child->baton, subpool));
        }
    }
  svn_pool_destroy(subpool);
  return SVN_NO_ERROR;
}
Exemplo n.º 16
0
/* Edit CHUNK and return the result in *MERGED_CHUNK allocated in POOL. */
static svn_error_t *
edit_chunk(apr_array_header_t **merged_chunk,
           apr_array_header_t *chunk,
           const char *editor_cmd,
           apr_hash_t *config,
           apr_pool_t *result_pool,
           apr_pool_t *scratch_pool)
{
  apr_file_t *temp_file;
  const char *temp_file_name;
  int i;
  apr_off_t pos;
  svn_boolean_t eof;
  svn_error_t *err;
  apr_pool_t *iterpool;

  SVN_ERR(svn_io_open_unique_file3(&temp_file, &temp_file_name, NULL,
                                   svn_io_file_del_on_pool_cleanup,
                                   scratch_pool, scratch_pool));
  iterpool = svn_pool_create(scratch_pool);
  for (i = 0; i < chunk->nelts; i++)
    {
      svn_stringbuf_t *line = APR_ARRAY_IDX(chunk, i, svn_stringbuf_t *);
      apr_size_t bytes_written;

      svn_pool_clear(iterpool);

      SVN_ERR(svn_io_file_write_full(temp_file, line->data, line->len,
                                     &bytes_written, iterpool));
      if (line->len != bytes_written)
        return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL,
                                _("Could not write data to temporary file"));
    }
  SVN_ERR(svn_io_file_flush(temp_file, scratch_pool));

  err = svn_cmdline__edit_file_externally(temp_file_name, editor_cmd,
                                          config, scratch_pool);
  if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR))
    {
      svn_error_t *root_err = svn_error_root_cause(err);

      SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
                                  root_err->message ? root_err->message :
                                  _("No editor found.")));
      svn_error_clear(err);
      *merged_chunk = NULL;
      svn_pool_destroy(iterpool);
      return SVN_NO_ERROR;
    }
  else if (err && (err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
    {
      svn_error_t *root_err = svn_error_root_cause(err);

      SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
                                  root_err->message ? root_err->message :
                                  _("Error running editor.")));
      svn_error_clear(err);
      *merged_chunk = NULL;
      svn_pool_destroy(iterpool);
      return SVN_NO_ERROR;
    }
  else if (err)
    return svn_error_trace(err);

  *merged_chunk = apr_array_make(result_pool, 1, sizeof(svn_stringbuf_t *));
  pos = 0;
  SVN_ERR(svn_io_file_seek(temp_file, APR_SET, &pos, scratch_pool));
  do
    {
      svn_stringbuf_t *line;
      const char *eol_str;

      svn_pool_clear(iterpool);

      SVN_ERR(svn_io_file_readline(temp_file, &line, &eol_str, &eof,
                                   APR_SIZE_MAX, result_pool, iterpool));
      if (eol_str)
        svn_stringbuf_appendcstr(line, eol_str);

      APR_ARRAY_PUSH(*merged_chunk, svn_stringbuf_t *) = line;
    }
  while (!eof);
  svn_pool_destroy(iterpool);

  SVN_ERR(svn_io_file_close(temp_file, scratch_pool));

  return SVN_NO_ERROR;
}
Exemplo n.º 17
0
svn_error_t *
svn_client_blame4(const char *target,
                  const svn_opt_revision_t *peg_revision,
                  const svn_opt_revision_t *start,
                  const svn_opt_revision_t *end,
                  const svn_diff_file_options_t *diff_options,
                  svn_boolean_t ignore_mime_type,
                  svn_boolean_t include_merged_revisions,
                  svn_client_blame_receiver2_t receiver,
                  void *receiver_baton,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *pool)
{
  struct file_rev_baton frb;
  svn_ra_session_t *ra_session;
  const char *url;
  svn_revnum_t start_revnum, end_revnum;
  struct blame *walk, *walk_merged = NULL;
  apr_file_t *file;
  apr_pool_t *iterpool;
  svn_stream_t *stream;

  if (start->kind == svn_opt_revision_unspecified
      || end->kind == svn_opt_revision_unspecified)
    return svn_error_create
      (SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
  else if (start->kind == svn_opt_revision_working
           || end->kind == svn_opt_revision_working)
    return svn_error_create
      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
       _("blame of the WORKING revision is not supported"));

  /* Get an RA plugin for this filesystem object. */
  SVN_ERR(svn_client__ra_session_from_path(&ra_session, &end_revnum,
                                           &url, target, NULL,
                                           peg_revision, end,
                                           ctx, pool));

  SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ra_session,
                                          start, target, pool));

  if (end_revnum < start_revnum)
    return svn_error_create
      (SVN_ERR_CLIENT_BAD_REVISION, NULL,
       _("Start revision must precede end revision"));

  frb.start_rev = start_revnum;
  frb.end_rev = end_revnum;
  frb.target = target;
  frb.ctx = ctx;
  frb.diff_options = diff_options;
  frb.ignore_mime_type = ignore_mime_type;
  frb.include_merged_revisions = include_merged_revisions;
  frb.last_filename = NULL;
  frb.last_original_filename = NULL;
  frb.chain = apr_palloc(pool, sizeof(*frb.chain));
  frb.chain->blame = NULL;
  frb.chain->avail = NULL;
  frb.chain->pool = pool;
  if (include_merged_revisions)
    {
      frb.merged_chain = apr_palloc(pool, sizeof(*frb.merged_chain));
      frb.merged_chain->blame = NULL;
      frb.merged_chain->avail = NULL;
      frb.merged_chain->pool = pool;
    }

  SVN_ERR(svn_io_temp_dir(&frb.tmp_path, pool));
  frb.tmp_path = svn_path_join(frb.tmp_path, "tmp", pool),

  frb.mainpool = pool;
  /* The callback will flip the following two pools, because it needs
     information from the previous call.  Obviously, it can't rely on
     the lifetime of the pool provided by get_file_revs. */
  frb.lastpool = svn_pool_create(pool);
  frb.currpool = svn_pool_create(pool);
  if (include_merged_revisions)
    {
      frb.filepool = svn_pool_create(pool);
      frb.prevfilepool = svn_pool_create(pool);
    }

  /* Collect all blame information.
     We need to ensure that we get one revision before the start_rev,
     if available so that we can know what was actually changed in the start
     revision. */
  SVN_ERR(svn_ra_get_file_revs2(ra_session, "",
                                start_revnum - (start_revnum > 0 ? 1 : 0),
                                end_revnum, include_merged_revisions,
                                file_rev_handler, &frb, pool));

  /* Report the blame to the caller. */

  /* The callback has to have been called at least once. */
  assert(frb.last_filename != NULL);

  /* Create a pool for the iteration below. */
  iterpool = svn_pool_create(pool);

  /* Open the last file and get a stream. */
  SVN_ERR(svn_io_file_open(&file, frb.last_filename, APR_READ | APR_BUFFERED,
                           APR_OS_DEFAULT, pool));
  stream = svn_subst_stream_translated(svn_stream_from_aprfile(file, pool),
                                       "\n", TRUE, NULL, FALSE, pool);

  /* Perform optional merged chain normalization. */
  if (include_merged_revisions)
    {
      /* If we never created any blame for the original chain, create it now,
         with the most recent changed revision.  This could occur if a file
         was created on a branch and them merged to another branch.  This is
         semanticly a copy, and we want to use the revision on the branch as
         the most recently changed revision.  ### Is this really what we want
         to do here?  Do the sematics of copy change? */
      if (!frb.chain->blame)
        frb.chain->blame = blame_create(frb.chain, frb.rev, 0);

      normalize_blames(frb.chain, frb.merged_chain, pool);
      walk_merged = frb.merged_chain->blame;
    }

  /* Process each blame item. */
  for (walk = frb.chain->blame; walk; walk = walk->next)
    {
      apr_off_t line_no;
      svn_revnum_t merged_rev;
      const char *merged_author, *merged_date, *merged_path;

      if (walk_merged)
        {
          merged_rev = walk_merged->rev->revision;
          merged_author = walk_merged->rev->author;
          merged_date = walk_merged->rev->date;
          merged_path = walk_merged->rev->path;
        }
      else
        {
          merged_rev = SVN_INVALID_REVNUM;
          merged_author = NULL;
          merged_date = NULL;
          merged_path = NULL;
        }

      for (line_no = walk->start;
           !walk->next || line_no < walk->next->start;
           ++line_no)
        {
          svn_boolean_t eof;
          svn_stringbuf_t *sb;

          svn_pool_clear(iterpool);
          SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, iterpool));
          if (ctx->cancel_func)
            SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
          if (!eof || sb->len)
            SVN_ERR(receiver(receiver_baton, line_no, walk->rev->revision,
                             walk->rev->author, walk->rev->date,
                             merged_rev, merged_author, merged_date,
                             merged_path, sb->data, iterpool));
          if (eof) break;
        }

      if (walk_merged)
        walk_merged = walk_merged->next;
    }

  SVN_ERR(svn_stream_close(stream));

  /* We don't need the temp file any more. */
  SVN_ERR(svn_io_file_close(file, pool));

  svn_pool_destroy(frb.lastpool);
  svn_pool_destroy(frb.currpool);
  if (include_merged_revisions)
    {
      svn_pool_destroy(frb.filepool);
      svn_pool_destroy(frb.prevfilepool);
    }
  svn_pool_destroy(iterpool);

  return SVN_NO_ERROR;
}
Exemplo n.º 18
0
static svn_error_t *
window_handler(svn_txdelta_window_t *window, void *baton)
{
  struct delta_baton *dbaton = baton;
  struct file_rev_baton *frb = dbaton->file_rev_baton;
  struct blame_chain *chain;

  /* Call the wrapped handler first. */
  SVN_ERR(dbaton->wrapped_handler(window, dbaton->wrapped_baton));

  /* We patiently wait for the NULL window marking the end. */
  if (window)
    return SVN_NO_ERROR;

  /* Close the files used for the delta.
     It is important to do this early, since otherwise, they will be deleted
     before all handles are closed, which leads to failures on some platforms
     when new tempfiles are to be created. */
  if (dbaton->source_file)
    SVN_ERR(svn_io_file_close(dbaton->source_file, frb->currpool));
  SVN_ERR(svn_io_file_close(dbaton->file, frb->currpool));

  /* If we are including merged revisions, we need to add each rev to the
     merged chain. */
  if (frb->include_merged_revisions)
    chain = frb->merged_chain;
  else
    chain = frb->chain;

  /* Process this file. */
  SVN_ERR(add_file_blame(frb->last_filename,
                         dbaton->filename, chain, frb->rev,
                         frb->diff_options, frb->currpool));

  /* If we are including merged revisions, and the current revision is not a
     merged one, we need to add its blame info to the chain for the original
     line of history. */
  if (frb->include_merged_revisions && ! frb->merged_revision)
    {
      apr_pool_t *tmppool;

      SVN_ERR(add_file_blame(frb->last_original_filename,
                             dbaton->filename, frb->chain, frb->rev,
                             frb->diff_options, frb->currpool));

      /* This filename could be around for a while, potentially, so
         use the longer lifetime pool, and switch it with the previous one*/
      svn_pool_clear(frb->prevfilepool);
      tmppool = frb->filepool;
      frb->filepool = frb->prevfilepool;
      frb->prevfilepool = tmppool;

      frb->last_original_filename = apr_pstrdup(frb->filepool,
                                                dbaton->filename);
    }

  /* Prepare for next revision. */

  /* Remember the file name so we can diff it with the next revision. */
  frb->last_filename = dbaton->filename;

  /* Switch pools. */
  {
    apr_pool_t *tmp_pool = frb->lastpool;
    frb->lastpool = frb->currpool;
    frb->currpool = tmp_pool;
  }

  return SVN_NO_ERROR;
}
Exemplo n.º 19
0
/* Internal version of svn_wc_merge, also used to (loggily) merge updates
   from the repository.

   In the case of updating, the update can have sent new properties,
   which could affect the way the wc target is detranslated and
   compared with LEFT and RIGHT for merging.

   Property changes sent by the update are provided in PROP_DIFF.

   If COPYFROM_TEXT is non-null, the working text is taken from this
   file instead of from the actual version in the working copy (and
   the merge_target is allowed to not be under version control in this
   case).
 */
svn_error_t *
svn_wc__merge_internal(svn_stringbuf_t **log_accum,
                       enum svn_wc_merge_outcome_t *merge_outcome,
                       const char *left,
                       const char *right,
                       const char *merge_target,
                       const char *copyfrom_text,
                       svn_wc_adm_access_t *adm_access,
                       const char *left_label,
                       const char *right_label,
                       const char *target_label,
                       svn_boolean_t dry_run,
                       const char *diff3_cmd,
                       const apr_array_header_t *merge_options,
                       const apr_array_header_t *prop_diff,
                       svn_wc_conflict_resolver_func_t conflict_func,
                       void *conflict_baton,
                       apr_pool_t *pool)
{
  const char *tmp_target, *result_target, *working_text;
  const char *adm_path = svn_wc_adm_access_path(adm_access);
  apr_file_t *result_f;
  svn_boolean_t is_binary = FALSE;
  const svn_wc_entry_t *entry;
  svn_boolean_t contains_conflicts;
  const svn_prop_t *mimeprop;

  /* Sanity check:  the merge target must be under revision control (unless
     this is an add-with-history). */
  SVN_ERR(svn_wc_entry(&entry, merge_target, adm_access, FALSE, pool));
  if (! entry && ! copyfrom_text)
    {
      *merge_outcome = svn_wc_merge_no_merge;
      return SVN_NO_ERROR;
    }

  /* Decide if the merge target is a text or binary file. */
  if ((mimeprop = get_prop(prop_diff, SVN_PROP_MIME_TYPE))
      && mimeprop->value)
    is_binary = svn_mime_type_is_binary(mimeprop->value->data);
  else if (! copyfrom_text)
    SVN_ERR(svn_wc_has_binary_prop(&is_binary, merge_target, adm_access, pool));

  working_text = copyfrom_text ? copyfrom_text : merge_target;
  SVN_ERR(detranslate_wc_file(&tmp_target, working_text, adm_access,
                              (! is_binary) && diff3_cmd != NULL,
                              prop_diff, pool));

  /* We cannot depend on the left file to contain the same eols as the
     right file. If the merge target has mods, this will mark the entire
     file as conflicted, so we need to compensate. */
  SVN_ERR(maybe_update_target_eols(&left, left, adm_access, prop_diff, pool));

  if (! is_binary)              /* this is a text file */
    {
      /* Open a second temporary file for writing; this is where diff3
         will write the merged results. */
      SVN_ERR(svn_wc_create_tmp_file2(&result_f, &result_target,
                                      adm_path, svn_io_file_del_none,
                                      pool));

      /* Run an external merge if requested. */
      if (diff3_cmd)
        {
          int exit_code;

          SVN_ERR(svn_io_run_diff3_2(&exit_code, ".",
                                     tmp_target, left, right,
                                     target_label, left_label, right_label,
                                     result_f, diff3_cmd,
                                     merge_options, pool));

          contains_conflicts = exit_code == 1;
        }
      else
        {
          svn_diff_t *diff;
          const char *target_marker;
          const char *left_marker;
          const char *right_marker;
          svn_stream_t *ostream;
          svn_diff_file_options_t *options;

          ostream = svn_stream_from_aprfile(result_f, pool);
          options = svn_diff_file_options_create(pool);

          if (merge_options)
            SVN_ERR(svn_diff_file_options_parse(options, merge_options, pool));

          SVN_ERR(svn_diff_file_diff3_2(&diff,
                                        left, tmp_target, right,
                                        options, pool));

          /* Labels fall back to sensible defaults if not specified. */
          if (target_label)
            target_marker = apr_psprintf(pool, "<<<<<<< %s", target_label);
          else
            target_marker = "<<<<<<< .working";

          if (left_label)
            left_marker = apr_psprintf(pool, "||||||| %s", left_label);
          else
            left_marker = "||||||| .old";

          if (right_label)
            right_marker = apr_psprintf(pool, ">>>>>>> %s", right_label);
          else
            right_marker = ">>>>>>> .new";

          SVN_ERR(svn_diff_file_output_merge(ostream, diff,
                                             left, tmp_target, right,
                                             left_marker,
                                             target_marker,
                                             right_marker,
                                             "=======", /* seperator */
                                             FALSE, /* display original */
                                             FALSE, /* resolve conflicts */
                                             pool));
          SVN_ERR(svn_stream_close(ostream));

          contains_conflicts = svn_diff_contains_conflicts(diff);
        }

      /* Close the output file */
      SVN_ERR(svn_io_file_close(result_f, pool));

      if (contains_conflicts && ! dry_run)  /* got a conflict */
        {
          const char *left_copy, *right_copy, *target_copy;
          const char *tmp_left, *tmp_right, *tmp_target_copy;
          const char *parentt, *target_base;
          svn_wc_adm_access_t *parent_access;
          svn_wc_entry_t tmp_entry;

          /* Give the conflict resolution callback a chance to clean
             up the conflict before we mark the file 'conflicted' */
          if (conflict_func)
            {
              svn_wc_conflict_result_t *result = NULL;
              svn_wc_conflict_description_t cdesc;

              cdesc.path = merge_target;
              cdesc.node_kind = svn_node_file;
              cdesc.kind = svn_wc_conflict_kind_text;
              cdesc.is_binary = FALSE;
              cdesc.mime_type = (mimeprop && mimeprop->value)
                                  ? mimeprop->value->data : NULL;
              cdesc.access = adm_access;
              cdesc.action = svn_wc_conflict_action_edit;
              cdesc.reason = svn_wc_conflict_reason_edited;
              cdesc.base_file = left;
              cdesc.their_file = right;
              cdesc.my_file = tmp_target;
              cdesc.merged_file = result_target;
              cdesc.property_name = NULL;

              SVN_ERR(conflict_func(&result, &cdesc, conflict_baton, pool));
              if (result == NULL)
                return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
                                        NULL, _("Conflict callback violated API:"
                                                " returned no results"));

              switch (result->choice)
                {
                  /* If the callback wants to use one of the fulltexts
                     to resolve the conflict, so be it.*/
                  case svn_wc_conflict_choose_base:
                    {
                      SVN_ERR(svn_wc__loggy_copy
                              (log_accum, NULL, adm_access,
                               svn_wc__copy_translate,
                               left, merge_target,
                               FALSE, pool));
                      *merge_outcome = svn_wc_merge_merged;
                      contains_conflicts = FALSE;
                      goto merge_complete;
                    }
                  case svn_wc_conflict_choose_theirs_full:
                    {
                      SVN_ERR(svn_wc__loggy_copy
                              (log_accum, NULL, adm_access,
                               svn_wc__copy_translate,
                               right, merge_target,
                               FALSE, pool));
                      *merge_outcome = svn_wc_merge_merged;
                      contains_conflicts = FALSE;
                      goto merge_complete;
                    }
                  case svn_wc_conflict_choose_mine_full:
                    {
                      /* Do nothing to merge_target, let it live untouched! */
                      *merge_outcome = svn_wc_merge_merged;
                      contains_conflicts = FALSE;
                      goto merge_complete;
                    }

                    /* For the case of 3-way file merging, we don't
                       really distinguish between these return values;
                       if the callback claims to have "generally
                       resolved" the situation, we still interpret
                       that as "OK, we'll assume the merged version is
                       good to use". */
                  case svn_wc_conflict_choose_merged:
                    {
                      SVN_ERR(svn_wc__loggy_copy
                              (log_accum, NULL, adm_access,
                               svn_wc__copy_translate,
                               /* Look for callback's own merged-file first: */
                               result->merged_file ?
                                  result->merged_file : result_target,
                               merge_target,
                               FALSE, pool));
                      *merge_outcome = svn_wc_merge_merged;
                      contains_conflicts = FALSE;
                      goto merge_complete;
                    }
                  case svn_wc_conflict_choose_postpone:
                  default:
                    {
                      /* Assume conflict remains, fall through to code below. */
                    }
                }
            }

          /* Preserve the three pre-merge files, and modify the
             entry (mark as conflicted, track the preserved files). */

          /* I miss Lisp. */

          SVN_ERR(svn_io_open_unique_file2(NULL,
                                           &left_copy,
                                           merge_target,
                                           left_label,
                                           svn_io_file_del_none,
                                           pool));

          /* Have I mentioned how much I miss Lisp? */

          SVN_ERR(svn_io_open_unique_file2(NULL,
                                           &right_copy,
                                           merge_target,
                                           right_label,
                                           svn_io_file_del_none,
                                           pool));

          /* Why, how much more pleasant to be forced to unroll my loops.
             If I'd been writing in Lisp, I might have mapped an inline
             lambda form over a list, or something equally disgusting.
             Thank goodness C was here to protect me! */

          SVN_ERR(svn_io_open_unique_file2(NULL,
                                           &target_copy,
                                           merge_target,
                                           target_label,
                                           svn_io_file_del_none,
                                           pool));

          /* We preserve all the files with keywords expanded and line
             endings in local (working) form. */

          svn_path_split(target_copy, &parentt, &target_base, pool);
          SVN_ERR(svn_wc_adm_retrieve(&parent_access, adm_access, parentt,
                                      pool));

          /* Log files require their paths to be in the subtree
             relative to the adm_access path they are executed in.

             Make our LEFT and RIGHT files 'local' if they aren't... */
          if (! svn_path_is_child(adm_path, left, pool))
            {
              SVN_ERR(svn_wc_create_tmp_file2
                      (NULL, &tmp_left,
                       adm_path, svn_io_file_del_none, pool));
              SVN_ERR(svn_io_copy_file(left, tmp_left, TRUE, pool));
            }
          else
            tmp_left = left;

          if (! svn_path_is_child(adm_path, right, pool))
            {
              SVN_ERR(svn_wc_create_tmp_file2
                      (NULL, &tmp_right,
                       adm_path, svn_io_file_del_none, pool));
              SVN_ERR(svn_io_copy_file(right, tmp_right, TRUE, pool));
            }
          else
            tmp_right = right;

          /* NOTE: Callers must ensure that the svn:eol-style and
             svn:keywords property values are correct in the currently
             installed props.  With 'svn merge', it's no big deal.  But
             when 'svn up' calls this routine, it needs to make sure that
             this routine is using the newest property values that may
             have been received *during* the update.  Since this routine
             will be run from within a log-command, merge_file()
             needs to make sure that a previous log-command to 'install
             latest props' has already executed first.  Ben and I just
             checked, and that is indeed the order in which the log items
             are written, so everything should be fine.  Really.  */

          /* Create LEFT and RIGHT backup files, in expanded form.
             We use merge_target's current properties to do the translation. */
          /* Derive the basenames of the 3 backup files. */
          SVN_ERR(svn_wc__loggy_translated_file(log_accum,
                                                adm_access,
                                                left_copy, tmp_left,
                                                merge_target, pool));
          SVN_ERR(svn_wc__loggy_translated_file(log_accum,
                                                adm_access,
                                                right_copy, tmp_right,
                                                merge_target, pool));

          /* Back up MERGE_TARGET through detranslation/retranslation:
             the new translation properties may not match the current ones */
          SVN_ERR(svn_wc_translated_file2(&tmp_target_copy,
                                          merge_target,
                                          merge_target,
                                          adm_access,
                                          SVN_WC_TRANSLATE_TO_NF
                                          | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
                                          pool));
          SVN_ERR(svn_wc__loggy_translated_file
                  (log_accum, adm_access,
                   target_copy, tmp_target_copy, merge_target, pool));

          tmp_entry.conflict_old
            = svn_path_is_child(adm_path, left_copy, pool);
          tmp_entry.conflict_new
            = svn_path_is_child(adm_path, right_copy, pool);
          tmp_entry.conflict_wrk = target_base;

          /* Mark merge_target's entry as "Conflicted", and start tracking
             the backup files in the entry as well. */
          SVN_ERR(svn_wc__loggy_entry_modify
                  (log_accum, adm_access,
                   merge_target, &tmp_entry,
                   SVN_WC__ENTRY_MODIFY_CONFLICT_OLD
                   | SVN_WC__ENTRY_MODIFY_CONFLICT_NEW
                   | SVN_WC__ENTRY_MODIFY_CONFLICT_WRK,
                   pool));

          *merge_outcome = svn_wc_merge_conflict;
        }
      else if (contains_conflicts && dry_run)
        {
          *merge_outcome = svn_wc_merge_conflict;
        } /* end of conflict handling */
      else if (copyfrom_text)
        {
          *merge_outcome = svn_wc_merge_merged;
        }
      else
        {
          svn_boolean_t same, special;
          /* If 'special', then use the detranslated form of the
             target file.  This is so we don't try to follow symlinks,
             but the same treatment is probably also appropriate for
             whatever special file types we may invent in the future. */
          SVN_ERR(svn_wc__get_special(&special, merge_target,
                                      adm_access, pool));
          SVN_ERR(svn_io_files_contents_same_p(&same, result_target,
                                               (special ?
                                                  tmp_target : 
                                                  merge_target),
                                               pool));

          *merge_outcome = same ? svn_wc_merge_unchanged : svn_wc_merge_merged;
        }

      if (*merge_outcome != svn_wc_merge_unchanged && ! dry_run)
        /* replace MERGE_TARGET with the new merged file, expanding. */
        SVN_ERR(svn_wc__loggy_copy(log_accum, NULL,
                                   adm_access,
                                   svn_wc__copy_translate,
                                   result_target, merge_target,
                                   FALSE, pool));

    } /* end of merging for text files */

  else if (! dry_run) /* merging procedure for binary files */
    {
      /* ### when making the binary-file backups, should we be honoring
         keywords and eol stuff?   */

      const char *left_copy, *right_copy;
      const char *parentt, *left_base, *right_base;
      svn_wc_entry_t tmp_entry;

      /* Give the conflict resolution callback a chance to clean
         up the conflict before we mark the file 'conflicted' */
      if (conflict_func)
        {
          svn_wc_conflict_result_t *result = NULL;
          svn_wc_conflict_description_t cdesc;

          cdesc.path = merge_target;
          cdesc.node_kind = svn_node_file;
          cdesc.kind = svn_wc_conflict_kind_text;
          cdesc.is_binary = TRUE;
          cdesc.mime_type = (mimeprop && mimeprop->value)
                                ? mimeprop->value->data : NULL;
          cdesc.access = adm_access;
          cdesc.action = svn_wc_conflict_action_edit;
          cdesc.reason = svn_wc_conflict_reason_edited;
          cdesc.base_file = left;
          cdesc.their_file = right;
          cdesc.my_file = tmp_target;
          cdesc.merged_file = NULL;     /* notice there is NO merged file! */
          cdesc.property_name = NULL;

          SVN_ERR(conflict_func(&result, &cdesc, conflict_baton, pool));
          if (result == NULL)
            return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
                                    NULL, _("Conflict callback violated API:"
                                            " returned no results"));

          switch (result->choice)
            {
              /* For a binary file, there's no merged file to look at,
                 unless the conflict-callback did the merging itself. */
              case svn_wc_conflict_choose_base:
                {
                  SVN_ERR(svn_wc__loggy_copy
                          (log_accum, NULL, adm_access,
                           svn_wc__copy_translate,
                           left, merge_target,
                           FALSE, pool));
                  *merge_outcome = svn_wc_merge_merged;
                  contains_conflicts = FALSE;
                  goto merge_complete;
                }
              case svn_wc_conflict_choose_theirs_full:
                {
                  SVN_ERR(svn_wc__loggy_copy
                          (log_accum, NULL, adm_access,
                           svn_wc__copy_translate,
                           right, merge_target,
                           FALSE, pool));
                  *merge_outcome = svn_wc_merge_merged;
                  contains_conflicts = FALSE;
                  goto merge_complete;
                }
                /* For a binary file, if the response is to use the
                   user's file, we do nothing.  We also do nothing if
                   the response claims to have already resolved the
                   problem.*/
              case svn_wc_conflict_choose_mine_full:
                {
                  *merge_outcome = svn_wc_merge_merged;
                  contains_conflicts = FALSE;
                  goto merge_complete;
                }
              case svn_wc_conflict_choose_merged:
                {
                  if (! result->merged_file)
                    {
                      /* Callback asked us to choose its own
                         merged file, but didn't provide one! */
                      return svn_error_create
                          (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
                           NULL, _("Conflict callback violated API:"
                                   " returned no merged file"));
                    }
                  else
                    {
                      SVN_ERR(svn_wc__loggy_copy
                              (log_accum, NULL, adm_access,
                               svn_wc__copy_translate,
                               result->merged_file, merge_target,
                               FALSE, pool));
                      *merge_outcome = svn_wc_merge_merged;
                      contains_conflicts = FALSE;
                      goto merge_complete;
                    }
                }
              case svn_wc_conflict_choose_postpone:
              default:
                {
                  /* Assume conflict remains, fall through to code below. */
                }
            }
        }

      /* reserve names for backups of left and right fulltexts */
      SVN_ERR(svn_io_open_unique_file2(NULL,
                                       &left_copy,
                                       merge_target,
                                       left_label,
                                       svn_io_file_del_none,
                                       pool));

      SVN_ERR(svn_io_open_unique_file2(NULL,
                                       &right_copy,
                                       merge_target,
                                       right_label,
                                       svn_io_file_del_none,
                                       pool));

      /* create the backup files */
      SVN_ERR(svn_io_copy_file(left,
                               left_copy, TRUE, pool));
      SVN_ERR(svn_io_copy_file(right,
                               right_copy, TRUE, pool));

      /* Was the merge target detranslated? */
      if (merge_target != tmp_target)
        {
          /* Create a .mine file too */
          const char *mine_copy;

          SVN_ERR(svn_io_open_unique_file2(NULL,
                                           &mine_copy,
                                           merge_target,
                                           target_label,
                                           svn_io_file_del_none,
                                           pool));
          SVN_ERR(svn_wc__loggy_move(log_accum, NULL,
                                     adm_access,
                                     tmp_target,
                                     mine_copy,
                                     FALSE, pool));
          mine_copy = svn_path_is_child(adm_path, mine_copy, pool);
          tmp_entry.conflict_wrk = mine_copy;
        }
      else
        tmp_entry.conflict_wrk = NULL;

      /* Derive the basenames of the backup files. */
      svn_path_split(left_copy, &parentt, &left_base, pool);
      svn_path_split(right_copy, &parentt, &right_base, pool);

      /* Mark merge_target's entry as "Conflicted", and start tracking
         the backup files in the entry as well. */
      tmp_entry.conflict_old = left_base;
      tmp_entry.conflict_new = right_base;
      SVN_ERR(svn_wc__loggy_entry_modify
              (log_accum,
               adm_access, merge_target,
               &tmp_entry,
               SVN_WC__ENTRY_MODIFY_CONFLICT_OLD
               | SVN_WC__ENTRY_MODIFY_CONFLICT_NEW
               | SVN_WC__ENTRY_MODIFY_CONFLICT_WRK,
               pool));

      *merge_outcome = svn_wc_merge_conflict; /* a conflict happened */

    } /* end of binary conflict handling */
  else
    *merge_outcome = svn_wc_merge_conflict; /* dry_run for binary files. */

  merge_complete:
  /* Merging is complete.  Regardless of text or binariness, we might
     need to tweak the executable bit on the new working file.  */
  if (! dry_run)
    {
      SVN_ERR(svn_wc__loggy_maybe_set_executable(log_accum,
                                                 adm_access, merge_target,
                                                 pool));

      SVN_ERR(svn_wc__loggy_maybe_set_readonly(log_accum,
                                                adm_access, merge_target,
                                                pool));

    }

  return SVN_NO_ERROR;
}
Exemplo n.º 20
0
/* Return a memblock of content, if any is available. *mem will be NULL if
   no further content is available. The memblock should eventually be
   passed to return_buffer() (or stored into buf->out_for_reading which
   will grab that block at the next get_buffer() call). */
static svn_error_t *
read_data(struct memblock_t **mem,
          svn_spillbuf_t *buf,
          apr_pool_t *scratch_pool)
{
  svn_error_t *err;

  /* If we have some in-memory blocks, then return one.  */
  if (buf->head != NULL)
    {
      *mem = buf->head;
      if (buf->tail == *mem)
        buf->head = buf->tail = NULL;
      else
        buf->head = (*mem)->next;

      /* We're using less memory now. If we haven't hit the spill file,
         then we may be able to keep using memory.  */
      buf->memory_size -= (*mem)->size;

      return SVN_NO_ERROR;
    }

  /* No file? Done.  */
  if (buf->spill == NULL)
    {
      *mem = NULL;
      return SVN_NO_ERROR;
    }

  /* Assume that the caller has seeked the spill file to the correct pos.  */

  /* Get a buffer that we can read content into.  */
  *mem = get_buffer(buf);
  /* NOTE: mem's size/next are uninitialized.  */

  if ((apr_uint64_t)buf->spill_size < (apr_uint64_t)buf->blocksize)
    (*mem)->size = (apr_size_t)buf->spill_size;
  else
    (*mem)->size = buf->blocksize;  /* The size of (*mem)->data  */
  (*mem)->next = NULL;

  /* Read some data from the spill file into the memblock.  */
  err = svn_io_file_read(buf->spill, (*mem)->data, &(*mem)->size,
                         scratch_pool);
  if (err)
    {
      return_buffer(buf, *mem);
      return svn_error_trace(err);
    }

  /* Mark the data that we consumed from the spill file.  */
  buf->spill_start += (*mem)->size;

  /* Did we consume all the data from the spill file?  */
  if ((buf->spill_size -= (*mem)->size) == 0)
    {
      /* Close and reset our spill file information.  */
      SVN_ERR(svn_io_file_close(buf->spill, scratch_pool));
      buf->spill = NULL;
      buf->spill_start = 0;
    }

  /* *mem has been initialized. Done.  */
  return SVN_NO_ERROR;
}
Exemplo n.º 21
0
int
main (int argc, const char **argv)
{
  apr_pool_t *pool;
  svn_error_t *err;
  apr_hash_t *dirents;
  const char *upload_file, *URL;
  const char *parent_URL, *basename;
  svn_ra_plugin_t *ra_lib;
  void *session, *ra_baton;
  svn_revnum_t rev;
  const svn_delta_editor_t *editor;
  void *edit_baton;
  svn_dirent_t *dirent;
  svn_ra_callbacks_t *cbtable;
  apr_hash_t *cfg_hash;
  svn_auth_baton_t *auth_baton;

  if (argc <= 2)
    {
      printf ("Usage:  %s PATH URL\n", argv[0]);
      printf ("    Uploads file at PATH to Subversion repository URL.\n");
      return EXIT_FAILURE;
    }
  upload_file = argv[1];
  URL = argv[2];

  /* Initialize the app.  Send all error messages to 'stderr'.  */
  if (svn_cmdline_init ("minimal_client", stderr) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  /* Create top-level memory pool. Be sure to read the HACKING file to
     understand how to properly use/free subpools. */
  pool = svn_pool_create (NULL);

  /* Initialize the FS library. */
  err = svn_fs_initialize (pool);
  if (err) goto hit_error;

  /* Make sure the ~/.subversion run-time config files exist, and load. */
  err = svn_config_ensure (NULL, pool);
  if (err) goto hit_error;

  err = svn_config_get_config (&cfg_hash, NULL, pool);
  if (err) goto hit_error;

  /* Build an authentication baton. */
  {
    /* There are many different kinds of authentication back-end
       "providers".  See svn_auth.h for a full overview. */
    svn_auth_provider_object_t *provider;
    apr_array_header_t *providers
      = apr_array_make (pool, 4, sizeof (svn_auth_provider_object_t *));

    svn_client_get_simple_prompt_provider (&provider,
                                           my_simple_prompt_callback,
                                           NULL, /* baton */
                                           2, /* retry limit */ pool);
    APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;

    svn_client_get_username_prompt_provider (&provider,
                                             my_username_prompt_callback,
                                             NULL, /* baton */
                                             2, /* retry limit */ pool);
    APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider;

    /* Register the auth-providers into the context's auth_baton. */
    svn_auth_open (&auth_baton, providers, pool);
  }

  /* Create a table of callbacks for the RA session, mostly nonexistent. */
  cbtable = apr_pcalloc (pool, sizeof(*cbtable));
  cbtable->auth_baton = auth_baton;
  cbtable->open_tmp_file = open_tmp_file;

  /* Now do the real work. */

  /* Open an RA session to the parent URL, fetch current HEAD rev and
     "lock" onto that revnum for the remainder of the session. */
  svn_path_split (URL, &parent_URL, &basename, pool);

  err = svn_ra_init_ra_libs (&ra_baton, pool);
  if (err) goto hit_error;

  err = svn_ra_get_ra_library (&ra_lib, ra_baton, parent_URL, pool);
  if (err) goto hit_error;

  err = ra_lib->open (&session, parent_URL, cbtable, NULL, cfg_hash, pool);
  if (err) goto hit_error;

  err = ra_lib->get_latest_revnum (session, &rev, pool);
  if (err) goto hit_error;

  /* Examine contents of parent dir in the rev. */
  err = ra_lib->get_dir (session, "", rev, &dirents, NULL, NULL, pool);
  if (err) goto hit_error;

  /* Sanity checks.  Don't let the user shoot himself *too* much. */
  dirent = apr_hash_get (dirents, basename, APR_HASH_KEY_STRING);
  if (dirent && dirent->kind == svn_node_dir)
    {
      printf ("Sorry, a directory already exists at that URL.\n");
      return EXIT_FAILURE;
    }
  if (dirent && dirent->kind == svn_node_file)
    {
      char answer[5];

      printf ("\n*** WARNING ***\n\n");
      printf ("You're about to overwrite r%ld of this file.\n", rev);
      printf ("It was last changed by user '%s',\n",
              dirent->last_author ? dirent->last_author : "?");
      printf ("on %s.\n", svn_time_to_human_cstring (dirent->time, pool));
      printf ("\nSomebody *might* have just changed the file seconds ago,\n"
              "and your upload would be overwriting their changes!\n\n");

      err = prompt_and_read_line("Are you SURE you want to upload? [y/n]",
                                 answer, sizeof(answer));
      if (err) goto hit_error;

      if (apr_strnatcasecmp (answer, "y"))
        {
          printf ("Operation aborted.\n");
          return EXIT_SUCCESS;
        }
    }

  /* Fetch a commit editor (it's anchored on the parent URL, because
     the session is too.) */
  /* ### someday add an option for a user-written commit message?  */
  err = ra_lib->get_commit_editor (session, &editor, &edit_baton,
                                   "File upload from 'svnput' program.",
                                   my_commit_callback, NULL, pool);
  if (err) goto hit_error;

  /* Drive the editor */
  {
    void *root_baton, *file_baton, *handler_baton;
    svn_txdelta_window_handler_t handler;
    svn_stream_t *contents;
    apr_file_t *f = NULL;

    err = editor->open_root (edit_baton, rev, pool, &root_baton);
    if (err) goto hit_error;

    if (! dirent)
      {
        err = editor->add_file (basename, root_baton, NULL, SVN_INVALID_REVNUM,
                                pool, &file_baton);
      }
    else
      {
        err = editor->open_file (basename, root_baton, rev, pool,
                                 &file_baton);
      }
    if (err) goto hit_error;

    err = editor->apply_textdelta (file_baton, NULL, pool,
                                   &handler, &handler_baton);
    if (err) goto hit_error;

    err = svn_io_file_open (&f, upload_file, APR_READ, APR_OS_DEFAULT, pool);
    if (err) goto hit_error;

    contents = svn_stream_from_aprfile (f, pool);
    err = svn_txdelta_send_stream (contents, handler, handler_baton,
                                   NULL, pool);
    if (err) goto hit_error;

    err = svn_io_file_close (f, pool);
    if (err) goto hit_error;

    err = editor->close_file (file_baton, NULL, pool);
    if (err) goto hit_error;

    err = editor->close_edit (edit_baton, pool);
    if (err) goto hit_error;
  }

  return EXIT_SUCCESS;

 hit_error:
  svn_handle_error2 (err, stderr, FALSE, "svnput: ");
  return EXIT_FAILURE;
}
Exemplo n.º 22
0
/* Return the transaction name of the activity stored in file
   PATHNAME, or NULL if PATHNAME cannot be read for any reason. */
static const char *
read_txn(const char *pathname, apr_pool_t *pool)
{
  apr_file_t *activity_file;
  apr_pool_t *iterpool = svn_pool_create(pool);
  apr_size_t len;
  svn_error_t *err = SVN_NO_ERROR;
  char *txn_name = apr_palloc(pool, SVN_FS__TXN_MAX_LEN+1);
  int i;

  /* Try up to 10 times to read the txn name, retrying on ESTALE
     (stale NFS file handle because of dav_svn__store_activity
     renaming the activity file into place).
   */
  for (i = 0; i < 10; i++)
    {
      svn_error_clear(err);
      svn_pool_clear(iterpool);

      err = svn_io_file_open(&activity_file, pathname,
                             APR_READ  | APR_BUFFERED,
                             APR_OS_DEFAULT, iterpool);
      if (err)
        {
#ifdef ESTALE
          if (APR_TO_OS_ERROR(err->apr_err) == ESTALE)
            /* Retry on ESTALE... */
            continue;
#endif
          /* ...else bail. */
          break;
        }

      len = SVN_FS__TXN_MAX_LEN;
      err = svn_io_read_length_line(activity_file, txn_name, &len, iterpool);
      if (err)
        {
#ifdef ESTALE
          if (APR_TO_OS_ERROR(err->apr_err) == ESTALE)
            continue;
#endif
          break;
        }

      err = svn_io_file_close(activity_file, iterpool);
#ifdef ESTALE
      if (err)
        {
          if (APR_TO_OS_ERROR(err->apr_err) == ESTALE)
            {
              /* No retry, just completely ignore this ESTALE. */
              svn_error_clear(err);
              err = SVN_NO_ERROR;
            }
        }
#endif

      /* We have a txn_name or had a non-ESTALE close failure; either
         way, we're finished. */
      break;
    }
  svn_pool_destroy(iterpool);

  /* ### let's just assume that any error means the
     ### activity/transaction doesn't exist */
  if (err)
    {
      svn_error_clear(err);
      return NULL;
    }

  return txn_name;
}
Exemplo n.º 23
0
Arquivo: fs.c Projeto: Alkzndr/freebsd
/* Write the DB_CONFIG file. */
static svn_error_t *
bdb_write_config(svn_fs_t *fs)
{
  const char *dbconfig_file_name =
    svn_dirent_join(fs->path, BDB_CONFIG_FILE, fs->pool);
  apr_file_t *dbconfig_file = NULL;
  int i;

  static const char dbconfig_contents[] =
    "# This is the configuration file for the Berkeley DB environment\n"
    "# used by your Subversion repository.\n"
    "# You must run 'svnadmin recover' whenever you modify this file,\n"
    "# for your changes to take effect.\n"
    "\n"
    "### Lock subsystem\n"
    "#\n"
    "# Make sure you read the documentation at:\n"
    "#\n"
    "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/lock_max.html\n"
    "#\n"
    "# before tweaking these values.\n"
    "#\n"
    "set_lk_max_locks   2000\n"
    "set_lk_max_lockers 2000\n"
    "set_lk_max_objects 2000\n"
    "\n"
    "### Log file subsystem\n"
    "#\n"
    "# Make sure you read the documentation at:\n"
    "#\n"
    "#   http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_bsize.html\n"
    "#   http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_max.html\n"
    "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_limits.html\n"
    "#\n"
    "# Increase the size of the in-memory log buffer from the default\n"
    "# of 32 Kbytes to 256 Kbytes.  Decrease the log file size from\n"
    "# 10 Mbytes to 1 Mbyte.  This will help reduce the amount of disk\n"
    "# space required for hot backups.  The size of the log file must be\n"
    "# at least four times the size of the in-memory log buffer.\n"
    "#\n"
    "# Note: Decreasing the in-memory buffer size below 256 Kbytes will hurt\n"
    "# hurt commit performance. For details, see:\n"
    "#\n"
    "#   http://svn.haxx.se/dev/archive-2002-02/0141.shtml\n"
    "#\n"
    "set_lg_bsize     262144\n"
    "set_lg_max      1048576\n"
    "#\n"
    "# If you see \"log region out of memory\" errors, bump lg_regionmax.\n"
    "# For more information, see:\n"
    "#\n"
    "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
    "#   http://svn.haxx.se/users/archive-2004-10/1000.shtml\n"
    "#\n"
    "set_lg_regionmax 131072\n"
    "#\n"
    /* ### Configure this with "svnadmin create --bdb-cache-size" */
    "# The default cache size in BDB is only 256k. As explained in\n"
    "# http://svn.haxx.se/dev/archive-2004-12/0368.shtml, this is too\n"
    "# small for most applications. Bump this number if \"db_stat -m\"\n"
    "# shows too many cache misses.\n"
    "#\n"
    "set_cachesize    0 1048576 1\n";

  /* Run-time configurable options.
     Each option set consists of a minimum required BDB version, a
     config hash key, a header, an inactive form and an active
     form. We always write the header; then, depending on the
     run-time configuration and the BDB version we're compiling
     against, we write either the active or inactive form of the
     value. */
  static const struct
  {
    int bdb_major;
    int bdb_minor;
    const char *config_key;
    const char *header;
    const char *inactive;
    const char *active;
  } dbconfig_options[] = {
    /* Controlled by "svnadmin create --bdb-txn-nosync" */
    { 4, 0, SVN_FS_CONFIG_BDB_TXN_NOSYNC,
      /* header */
      "#\n"
      "# Disable fsync of log files on transaction commit. Read the\n"
      "# documentation about DB_TXN_NOSYNC at:\n"
      "#\n"
      "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
      "#\n"
      "# [requires Berkeley DB 4.0]\n"
      "#\n",
      /* inactive */
      "#set_flags DB_TXN_NOSYNC\n",
      /* active */
      "set_flags DB_TXN_NOSYNC\n" },
    /* Controlled by "svnadmin create --bdb-log-keep" */
    { 4, 2, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE,
      /* header */
      "#\n"
      "# Enable automatic removal of unused transaction log files.\n"
      "# Read the documentation about DB_LOG_AUTOREMOVE at:\n"
      "#\n"
      "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
      "#\n"
      "# [requires Berkeley DB 4.2]\n"
      "#\n",
      /* inactive */
      "#set_flags DB_LOG_AUTOREMOVE\n",
      /* active */
      "set_flags DB_LOG_AUTOREMOVE\n" },
  };
  static const int dbconfig_options_length =
    sizeof(dbconfig_options)/sizeof(*dbconfig_options);


  SVN_ERR(svn_io_file_open(&dbconfig_file, dbconfig_file_name,
                           APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
                           fs->pool));

  SVN_ERR(svn_io_file_write_full(dbconfig_file, dbconfig_contents,
                                 sizeof(dbconfig_contents) - 1, NULL,
                                 fs->pool));

  /* Write the variable DB_CONFIG flags. */
  for (i = 0; i < dbconfig_options_length; ++i)
    {
      void *value = NULL;
      const char *choice;

      if (fs->config)
        {
          value = svn_hash_gets(fs->config, dbconfig_options[i].config_key);
        }

      SVN_ERR(svn_io_file_write_full(dbconfig_file,
                                     dbconfig_options[i].header,
                                     strlen(dbconfig_options[i].header),
                                     NULL, fs->pool));

      if (((DB_VERSION_MAJOR == dbconfig_options[i].bdb_major
            && DB_VERSION_MINOR >= dbconfig_options[i].bdb_minor)
           || DB_VERSION_MAJOR > dbconfig_options[i].bdb_major)
          && value != NULL && strcmp(value, "0") != 0)
        choice = dbconfig_options[i].active;
      else
        choice = dbconfig_options[i].inactive;

      SVN_ERR(svn_io_file_write_full(dbconfig_file, choice, strlen(choice),
                                     NULL, fs->pool));
    }

  return svn_io_file_close(dbconfig_file, fs->pool);
}
Exemplo n.º 24
0
Arquivo: fs.c Projeto: Alkzndr/freebsd
/* Copy FILENAME from SRC_DIR to DST_DIR in byte increments of size
   CHUNKSIZE.  The read/write buffer of size CHUNKSIZE will be
   allocated in POOL.  If ALLOW_MISSING is set, we won't make a fuss
   if FILENAME isn't found in SRC_DIR; otherwise, we will.  */
static svn_error_t *
copy_db_file_safely(const char *src_dir,
                    const char *dst_dir,
                    const char *filename,
                    u_int32_t chunksize,
                    svn_boolean_t allow_missing,
                    apr_pool_t *pool)
{
  apr_file_t *s = NULL, *d = NULL;  /* init to null important for APR */
  const char *file_src_path = svn_dirent_join(src_dir, filename, pool);
  const char *file_dst_path = svn_dirent_join(dst_dir, filename, pool);
  svn_error_t *err;
  char *buf;

  /* Open source file.  If it's missing and that's allowed, there's
     nothing more to do here. */
  err = svn_io_file_open(&s, file_src_path,
                         (APR_READ | APR_LARGEFILE),
                         APR_OS_DEFAULT, pool);
  if (err && APR_STATUS_IS_ENOENT(err->apr_err) && allow_missing)
    {
      svn_error_clear(err);
      return SVN_NO_ERROR;
    }
  SVN_ERR(err);

  /* Open destination file. */
  SVN_ERR(svn_io_file_open(&d, file_dst_path, (APR_WRITE | APR_CREATE |
                                               APR_LARGEFILE),
                           APR_OS_DEFAULT, pool));

  /* Allocate our read/write buffer. */
  buf = apr_palloc(pool, chunksize);

  /* Copy bytes till the cows come home. */
  while (1)
    {
      apr_size_t bytes_this_time = chunksize;
      svn_error_t *read_err, *write_err;

      /* Read 'em. */
      if ((read_err = svn_io_file_read(s, buf, &bytes_this_time, pool)))
        {
          if (APR_STATUS_IS_EOF(read_err->apr_err))
            svn_error_clear(read_err);
          else
            {
              svn_error_clear(svn_io_file_close(s, pool));
              svn_error_clear(svn_io_file_close(d, pool));
              return read_err;
            }
        }

      /* Write 'em. */
      if ((write_err = svn_io_file_write_full(d, buf, bytes_this_time, NULL,
                                              pool)))
        {
          svn_error_clear(svn_io_file_close(s, pool));
          svn_error_clear(svn_io_file_close(d, pool));
          return write_err;
        }

      /* read_err is either NULL, or a dangling pointer - but it is only a
         dangling pointer if it used to be an EOF error. */
      if (read_err)
        {
          SVN_ERR(svn_io_file_close(s, pool));
          SVN_ERR(svn_io_file_close(d, pool));
          break;  /* got EOF on read, all files closed, all done. */
        }
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 25
0
static svn_error_t *
do_blame (const char *target, 
           svn_ra_plugin_t *ra_lib,
           void *ra_session,
           struct file_rev_baton *frb)
{
  struct log_message_baton lmb;
  apr_array_header_t *condensed_targets;
  apr_file_t *file;
  svn_stream_t *stream;
  struct rev *rev;
  svn_node_kind_t kind;
  apr_pool_t *pool = frb->mainpool;

  SVN_ERR (ra_lib->check_path (ra_session, target, frb->end_rev, &kind, pool));

  if (kind == svn_node_dir) 
    return svn_error_createf (SVN_ERR_CLIENT_IS_DIRECTORY, NULL,
                              ("URL '%s' refers to a directory"), target); 

  condensed_targets = apr_array_make (pool, 1, sizeof (const char *));
  (*((const char **)apr_array_push (condensed_targets))) = "";

  lmb.path = apr_pstrcat(pool, "/", target, NULL);
  lmb.eldest = NULL;
  lmb.pool = pool;

  /* Accumulate revision metadata by walking the revisions
     backwards; this allows us to follow moves/copies
     correctly. */
  SVN_ERR (ra_lib->get_log (ra_session,
                            condensed_targets,
                            frb->end_rev,
                            frb->start_rev,
                            TRUE,
                            FALSE,
                            log_message_receiver,
                            &lmb,
                            pool));

  /* Inspect the first revision's change metadata; if there are any
     prior revisions, compute a new starting revision/path.  If no
     revisions were selected, no blame is assigned.  A modified
     item certainly has a prior revision.  It is reasonable for an
     added item to have none, but anything else is unexpected.  */
  if (!lmb.eldest)
    {
      lmb.eldest = apr_palloc (pool, sizeof (*rev));
      lmb.eldest->revision = frb->end_rev;
      lmb.eldest->path = lmb.path;
      lmb.eldest->next = NULL;
      rev = apr_palloc (pool, sizeof (*rev));
      rev->revision = SVN_INVALID_REVNUM;
      rev->author = NULL;
      rev->date = NULL;
      frb->blame = blame_create (frb, rev, 0);
    }
  else if (lmb.action == 'M' || SVN_IS_VALID_REVNUM (lmb.copyrev))
    {
      rev = apr_palloc (pool, sizeof (*rev));
      if (SVN_IS_VALID_REVNUM (lmb.copyrev))
        rev->revision = lmb.copyrev;
      else
        rev->revision = lmb.eldest->revision - 1;
      rev->path = lmb.path;
      rev->next = lmb.eldest;
      lmb.eldest = rev;
      rev = apr_palloc (pool, sizeof (*rev));
      rev->revision = SVN_INVALID_REVNUM;
      rev->author = NULL;
      rev->date = NULL;
      frb->blame = blame_create (frb, rev, 0);
    }
  else if (lmb.action == 'A')
    {
      frb->blame = blame_create (frb, lmb.eldest, 0);
    }
  else
    return svn_error_createf (APR_EGENERAL, NULL,
                              ("Revision action '%c' for "
                                "revision %ld of '%s' "
                                "lacks a prior revision"),
                              lmb.action, lmb.eldest->revision,
                              lmb.eldest->path);

  /* Walk the revision list in chronological order, downloading
     each fulltext, diffing it with its predecessor, and accumulating
     the blame information into db.blame.  Use two iteration pools
     rather than one, because the diff routines need to look at a
     sliding window of revisions.  Two pools gives us a ring buffer
     of sorts. */
  for (rev = lmb.eldest; rev; rev = rev->next)
    {
      const char *tmp;
      const char *temp_dir;
      apr_hash_t *props;
      svn_string_t *mimetype;
      
      apr_pool_clear (frb->currpool);
      SVN_ERR (svn_io_temp_dir (&temp_dir, frb->currpool));
      SVN_ERR (svn_io_open_unique_file (&file, &tmp,
                 svn_path_join (temp_dir, "tmp", frb->currpool), ".tmp",
                                        FALSE, frb->currpool));

      apr_pool_cleanup_register (frb->currpool, file, cleanup_tempfile,
                                 apr_pool_cleanup_null);

      stream = svn_stream_from_aprfile (file, frb->currpool);
      SVN_ERR (ra_lib->get_file(ra_session, rev->path + 1, rev->revision,
                                stream, NULL, &props, frb->currpool));
      SVN_ERR (svn_stream_close (stream));
      SVN_ERR (svn_io_file_close (file, frb->currpool));

      /* If this file has a non-textual mime-type, bail out. */
      if (props && 
          ((mimetype = apr_hash_get (props, SVN_PROP_MIME_TYPE, 
                                     sizeof (SVN_PROP_MIME_TYPE) - 1))))
        {
          if (svn_mime_type_is_binary (mimetype->data))
            return svn_error_createf (SVN_ERR_CLIENT_IS_BINARY_FILE, 0,
               ("Cannot calculate blame information for binary file '%s'"),target);
        }

      if (frb->last_filename)
        {
          frb->rev = rev;
          SVN_ERR (add_file_blame (frb->last_filename, tmp, frb));
        }

      frb->last_filename = tmp;
      {
        apr_pool_t *tmppool = frb->currpool;
        frb->currpool = frb->lastpool;
        frb->lastpool = tmppool;
      }
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 26
0
svn_error_t *
svn_ra_blame (svn_ra_plugin_t *ra_lib, void *ra_session,
              const char *target,
              svn_revnum_t start_revnum,
              svn_revnum_t end_revnum,
              svn_client_blame_receiver_t receiver,
              void *receiver_baton,
              apr_pool_t *pool)
{
    struct file_rev_baton frb;
    struct blame *walk;
    apr_file_t *tempfile;
    apr_pool_t *iterpool;
    svn_stream_t *stream;
    svn_error_t *err;

    // SVN_INVALID_REVNUM (=head revision)
    if ((end_revnum < start_revnum) && (end_revnum != SVN_INVALID_REVNUM))
      return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
                              ("Start revision must precede end revision"));

    frb.start_rev = start_revnum;
    frb.end_rev = end_revnum;
    frb.target = target;
    frb.last_filename = NULL;
    frb.blame = NULL;
    frb.avail = NULL;
    frb.mainpool = pool;

  /* The callback will flip the following two pools, because it needs
     information from the previous call.  Obviously, it can't rely on
     the lifetime of the pool provided by get_file_revs. */
    frb.lastpool = svn_pool_create (pool);
    frb.currpool = svn_pool_create (pool);

    // do the actual blame
    err = do_blame (target, ra_lib, ra_session, &frb);
    if (err) return err;

    /* Report the blame to the caller. */

    /* The callback has to have been called at least once. */
    assert (frb.last_filename != NULL);

    /* Create a pool for the iteration below. */
    iterpool = svn_pool_create (pool);

    /* Open the last file and get a stream. */
    err = svn_io_file_open (&tempfile, frb.last_filename, APR_READ, APR_OS_DEFAULT, pool);
    if (err) return err;
    stream = svn_stream_from_aprfile(tempfile, pool);

    /* Process each blame item. */
    for (walk = frb.blame; walk; walk = walk->next)
    {
        apr_off_t line_no;
        for (line_no = walk->start; !walk->next || line_no < walk->next->start; ++line_no)
        {
            svn_boolean_t eof;
            svn_stringbuf_t *sb;
            apr_pool_clear (iterpool);
            err = svn_stream_readline (stream, &sb, "\n", &eof, iterpool);
            if (err) return err;

            if (!eof || sb->len)
                SVN_ERR (receiver (receiver_baton, line_no, walk->rev->revision,
                         walk->rev->author, walk->rev->date,
                         sb->data, iterpool));
            if (eof) break;
        }
    }

    err = svn_stream_close (stream);
    err = svn_io_file_close(tempfile, pool);
    svn_pool_destroy(frb.lastpool);
    svn_pool_destroy(frb.currpool);
    svn_pool_destroy(iterpool);
    return SVN_NO_ERROR;
}
Exemplo n.º 27
0
static svn_error_t *
pack_filesystem(const svn_test_opts_t *opts,
                apr_pool_t *pool)
{
  int i;
  svn_node_kind_t kind;
  const char *path;
  char buf[80];
  apr_file_t *file;
  apr_size_t len;

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

  SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
                                   pool));

  /* Check to see that the pack files exist, and that the rev directories
     don't. */
  for (i = 0; i < (MAX_REV + 1) / SHARD_SIZE; i++)
    {
      path = svn_dirent_join_many(pool, REPO_NAME, "revs",
                                  apr_psprintf(pool, "%d.pack", i / SHARD_SIZE),
                                  "pack", NULL);

      /* These files should exist. */
      SVN_ERR(svn_io_check_path(path, &kind, pool));
      if (kind != svn_node_file)
        return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
                                 "Expected pack file '%s' not found", path);

      path = svn_dirent_join_many(pool, REPO_NAME, "revs",
                                  apr_psprintf(pool, "%d.pack", i / SHARD_SIZE),
                                  "manifest", NULL);
      SVN_ERR(svn_io_check_path(path, &kind, pool));
      if (kind != svn_node_file)
        return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
                                 "Expected manifest file '%s' not found",
                                 path);

      /* This directory should not exist. */
      path = svn_dirent_join_many(pool, REPO_NAME, "revs",
                                  apr_psprintf(pool, "%d", i / SHARD_SIZE),
                                  NULL);
      SVN_ERR(svn_io_check_path(path, &kind, pool));
      if (kind != svn_node_none)
        return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
                                 "Unexpected directory '%s' found", path);
    }

  /* Ensure the min-unpacked-rev jives with the above operations. */
  SVN_ERR(svn_io_file_open(&file,
                           svn_dirent_join(REPO_NAME, PATH_MIN_UNPACKED_REV,
                                           pool),
                           APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
  len = sizeof(buf);
  SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
  SVN_ERR(svn_io_file_close(file, pool));
  if (SVN_STR_TO_REV(buf) != (MAX_REV / SHARD_SIZE) * SHARD_SIZE)
    return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
                             "Bad '%s' contents", PATH_MIN_UNPACKED_REV);

  /* Finally, make sure the final revision directory does exist. */
  path = svn_dirent_join_many(pool, REPO_NAME, "revs",
                              apr_psprintf(pool, "%d", (i / SHARD_SIZE) + 1),
                              NULL);
  SVN_ERR(svn_io_check_path(path, &kind, pool));
  if (kind != svn_node_none)
    return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
                             "Expected directory '%s' not found", path);


  return SVN_NO_ERROR;
}