예제 #1
0
/* The main logic of the public svn_client_add4.
 *
 * EXISTING_PARENT_ABSPATH is the absolute path to the first existing
 * parent directory of local_abspath. If not NULL, all missing parents
 * of LOCAL_ABSPATH must be created before LOCAL_ABSPATH can be added. */
static svn_error_t *
add(const char *local_abspath,
    svn_depth_t depth,
    svn_boolean_t force,
    svn_boolean_t no_ignore,
    const char *existing_parent_abspath,
    svn_client_ctx_t *ctx,
    apr_pool_t *scratch_pool)
{
  svn_node_kind_t kind;
  svn_error_t *err;
  svn_magic__cookie_t *magic_cookie;

  svn_magic__init(&magic_cookie, scratch_pool);

  if (existing_parent_abspath)
    {
      const char *parent_abspath;
      const char *child_relpath;
      apr_array_header_t *components;
      int i;
      apr_pool_t *iterpool;

      parent_abspath = existing_parent_abspath;
      child_relpath = svn_dirent_is_child(existing_parent_abspath,
                                          local_abspath, NULL);
      components = svn_path_decompose(child_relpath, scratch_pool);
      iterpool = svn_pool_create(scratch_pool);
      for (i = 0; i < components->nelts - 1; i++)
        {
          const char *component;
          svn_node_kind_t disk_kind;

          svn_pool_clear(iterpool);

          if (ctx->cancel_func)
            SVN_ERR(ctx->cancel_func(ctx->cancel_baton));

          component = APR_ARRAY_IDX(components, i, const char *);
          parent_abspath = svn_dirent_join(parent_abspath, component,
                                           scratch_pool);
          SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, iterpool));
          if (disk_kind != svn_node_none && disk_kind != svn_node_dir)
            return svn_error_createf(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL,
                                     _("'%s' prevents creating parent of '%s'"),
                                     parent_abspath, local_abspath);

          SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool));
          SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, parent_abspath,
                                       ctx->notify_func2, ctx->notify_baton2,
                                       scratch_pool));
        }
      svn_pool_destroy(iterpool);
    }

  SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
  if (kind == svn_node_dir)
    {
      /* We use add_dir_recursive for all directory targets
         and pass depth along no matter what it is, so that the
         target's depth will be set correctly. */
      err = add_dir_recursive(local_abspath, depth, force, no_ignore,
                              magic_cookie, ctx, scratch_pool);
    }
  else if (kind == svn_node_file)
    err = add_file(local_abspath, magic_cookie, ctx, scratch_pool);
  else if (kind == svn_node_none)
    {
      svn_boolean_t tree_conflicted;

      /* Provide a meaningful error message if the node does not exist
       * on disk but is a tree conflict victim. */
      err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted,
                                 ctx->wc_ctx, local_abspath,
                                 scratch_pool);
      if (err)
        svn_error_clear(err);
      else if (tree_conflicted)
        return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
                                 _("'%s' is an existing item in conflict; "
                                   "please mark the conflict as resolved "
                                   "before adding a new item here"),
                                 svn_dirent_local_style(local_abspath,
                                                        scratch_pool));

      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
                               _("'%s' not found"),
                               svn_dirent_local_style(local_abspath,
                                                      scratch_pool));
    }
  else
    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                             _("Unsupported node kind for path '%s'"),
                             svn_dirent_local_style(local_abspath,
                                                    scratch_pool));

  /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set.  */
  if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
    {
      svn_error_clear(err);
      err = SVN_NO_ERROR;
    }
  return svn_error_trace(err);
}
예제 #2
0
static svn_error_t *
new_node_record(void **node_baton,
                apr_hash_t *headers,
                void *revision_baton,
                apr_pool_t *pool)
{
  struct revision_baton *rb = revision_baton;
  const struct svn_delta_editor_t *commit_editor = rb->pb->commit_editor;
  void *commit_edit_baton = rb->pb->commit_edit_baton;
  struct node_baton *nb;
  apr_hash_index_t *hi;
  void *child_baton;
  const char *nb_dirname;

  nb = apr_pcalloc(rb->pool, sizeof(*nb));
  nb->rb = rb;
  nb->is_added = FALSE;
  nb->copyfrom_path = NULL;
  nb->copyfrom_url = NULL;
  nb->copyfrom_rev = SVN_INVALID_REVNUM;
  nb->prop_changes = apr_hash_make(rb->pool);

  /* If the creation of commit_editor is pending, create it now and
     open_root on it; also create a top-level directory baton. */

  if (!commit_editor)
    {
      /* The revprop_table should have been filled in with important
         information like svn:log in set_revision_property. We can now
         use it all this information to create our commit_editor. But
         first, clear revprops that we aren't allowed to set with the
         commit_editor. We'll set them separately using the RA API
         after closing the editor (see close_revision). */

      svn_hash_sets(rb->revprop_table, SVN_PROP_REVISION_AUTHOR, NULL);
      svn_hash_sets(rb->revprop_table, SVN_PROP_REVISION_DATE, NULL);

      SVN_ERR(svn_ra__register_editor_shim_callbacks(rb->pb->session,
                                    get_shim_callbacks(rb, rb->pool)));
      SVN_ERR(svn_ra_get_commit_editor3(rb->pb->session, &commit_editor,
                                        &commit_edit_baton, rb->revprop_table,
                                        commit_callback, revision_baton,
                                        NULL, FALSE, rb->pool));

      rb->pb->commit_editor = commit_editor;
      rb->pb->commit_edit_baton = commit_edit_baton;

      SVN_ERR(commit_editor->open_root(commit_edit_baton,
                                       rb->rev - rb->rev_offset - 1,
                                       rb->pool, &child_baton));

      LDR_DBG(("Opened root %p\n", child_baton));

      /* child_baton corresponds to the root directory baton here */
      push_directory(rb, child_baton, "", TRUE /*is_added*/,
                     NULL, SVN_INVALID_REVNUM);
    }

  for (hi = apr_hash_first(rb->pool, headers); hi; hi = apr_hash_next(hi))
    {
      const char *hname = svn__apr_hash_index_key(hi);
      const char *hval = svn__apr_hash_index_val(hi);

      /* Parse the different kinds of headers we can encounter and
         stuff them into the node_baton for writing later */
      if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_PATH) == 0)
        nb->path = apr_pstrdup(rb->pool, hval);
      if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_KIND) == 0)
        nb->kind = strcmp(hval, "file") == 0 ? svn_node_file : svn_node_dir;
      if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_ACTION) == 0)
        {
          if (strcmp(hval, "add") == 0)
            nb->action = svn_node_action_add;
          if (strcmp(hval, "change") == 0)
            nb->action = svn_node_action_change;
          if (strcmp(hval, "delete") == 0)
            nb->action = svn_node_action_delete;
          if (strcmp(hval, "replace") == 0)
            nb->action = svn_node_action_replace;
        }
      if (strcmp(hname, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5) == 0)
        nb->base_checksum = apr_pstrdup(rb->pool, hval);
      if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV) == 0)
        nb->copyfrom_rev = atoi(hval);
      if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH) == 0)
        nb->copyfrom_path = apr_pstrdup(rb->pool, hval);
    }

  nb_dirname = svn_relpath_dirname(nb->path, pool);
  if (svn_path_compare_paths(nb_dirname,
                             rb->db->relpath) != 0)
    {
      char *ancestor_path;
      apr_size_t residual_close_count;
      apr_array_header_t *residual_open_path;
      int i;
      apr_size_t n;

      /* Before attempting to handle the action, call open_directory
         for all the path components and set the directory baton
         accordingly */
      ancestor_path =
        svn_relpath_get_longest_ancestor(nb_dirname,
                                         rb->db->relpath, pool);
      residual_close_count =
        svn_path_component_count(svn_relpath_skip_ancestor(ancestor_path,
                                                           rb->db->relpath));
      residual_open_path =
        svn_path_decompose(svn_relpath_skip_ancestor(ancestor_path,
                                                     nb_dirname), pool);

      /* First close all as many directories as there are after
         skip_ancestor, and then open fresh directories */
      for (n = 0; n < residual_close_count; n ++)
        {
          /* Don't worry about destroying the actual rb->db object,
             since the pool we're using has the lifetime of one
             revision anyway */
          LDR_DBG(("Closing dir %p\n", rb->db->baton));
          SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool));
          rb->db = rb->db->parent;
        }

      for (i = 0; i < residual_open_path->nelts; i ++)
        {
          char *relpath_compose =
            svn_relpath_join(rb->db->relpath,
                             APR_ARRAY_IDX(residual_open_path, i, const char *),
                             rb->pool);
          SVN_ERR(commit_editor->open_directory(relpath_compose,
                                                rb->db->baton,
                                                rb->rev - rb->rev_offset - 1,
                                                rb->pool, &child_baton));
          LDR_DBG(("Opened dir %p\n", child_baton));
          push_directory(rb, child_baton, relpath_compose, TRUE /*is_added*/,
                         NULL, SVN_INVALID_REVNUM);
        }
    }
예제 #3
0
/* Add PATH to the operations tree rooted at OPERATION, creating any
   intermediate nodes that are required.  Here's what's expected for
   each action type:

      ACTION          URL    REV      SRC-FILE  PROPNAME
      ------------    -----  -------  --------  --------
      ACTION_MKDIR    NULL   invalid  NULL      NULL
      ACTION_CP       valid  valid    NULL      NULL
      ACTION_PUT      NULL   invalid  valid     NULL
      ACTION_RM       NULL   invalid  NULL      NULL
      ACTION_PROPSET  valid  invalid  NULL      valid

   Node type information is obtained for any copy source (to determine
   whether to create a file or directory) and for any deleted path (to
   ensure it exists since svn_delta_editor_t->delete_entry doesn't
   return an error on non-existent nodes). */
static svn_error_t *
build(struct editor_baton *eb,
      action_code_t action,
      const char *relpath,
      const char *url,
      svn_revnum_t rev,
      apr_hash_t *props,
      const char *src_file,
      svn_revnum_t head,
      apr_pool_t *scratch_pool)
{
  apr_array_header_t *path_bits = svn_path_decompose(relpath, scratch_pool);
  const char *path_so_far = "";
  struct operation *operation = &eb->root;
  int i;

  /* We should only see PROPS when action is ACTION_PROPSET. */
  SVN_ERR_ASSERT((props && action == ACTION_PROPSET)
                || (!props && action != ACTION_PROPSET) );

  /* Look for any previous operations we've recognized for PATH.  If
     any of PATH's ancestors have not yet been traversed, we'll be
     creating OP_OPEN operations for them as we walk down PATH's path
     components. */
  for (i = 0; i < path_bits->nelts; ++i)
    {
      const char *path_bit = APR_ARRAY_IDX(path_bits, i, const char *);
      path_so_far = svn_relpath_join(path_so_far, path_bit, scratch_pool);
      operation = get_operation(path_so_far, operation, eb->edit_pool);
    }

  /* Handle property changes. */
  if (props)
    {
      SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
                                  relpath, scratch_pool));

      /* If we're not adding this thing ourselves, check for existence.  */
      if (! ((operation->operation == OP_ADD) ||
             (operation->operation == OP_REPLACE)))
        {
          if ((operation->kind == svn_node_file)
                   && (operation->operation == OP_OPEN))
            operation->operation = OP_PROPSET;
        }
      operation->props = svn_prop_hash_dup(props, eb->edit_pool);
      if (!operation->rev)
        operation->rev = rev;
      return SVN_NO_ERROR;
    }

  if (action == ACTION_RM)
    operation->operation = OP_DELETE;

  /* Handle copy operations (which can be adds or replacements). */
  else if (action == ACTION_CP)
    {
      operation->operation =
        operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;

      SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
                                  relpath, scratch_pool));
      operation->url = url;
      operation->rev = rev;
    }
  /* Handle mkdir operations (which can be adds or replacements). */
  else if (action == ACTION_MKDIR)
    {
      operation->operation =
        operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
      operation->kind = svn_node_dir;
    }
  /* Handle put operations (which can be adds, replacements, or opens). */
  else if (action == ACTION_PUT)
    {
      if (operation->operation == OP_DELETE)
        {
          operation->operation = OP_REPLACE;
        }
      else
        {
          SVN_ERR(eb->fetch_kind_func(&operation->kind, eb->fetch_kind_baton,
                                      relpath, scratch_pool));
          if (operation->kind == svn_node_file)
            operation->operation = OP_OPEN;
          else if (operation->kind == svn_node_none)
            operation->operation = OP_ADD;
          else
            return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                     "'%s' is not a file", relpath);
        }
      operation->kind = svn_node_file;
      operation->src_file = src_file;
    }
  else
    {
      /* We shouldn't get here. */
      SVN_ERR_MALFUNCTION();
    }

  return SVN_NO_ERROR;
}
예제 #4
0
CStringA CPathUtils::GetAbsoluteURL
    ( const CStringA& URL
    , const CStringA& repositoryRootURL
    , const CStringA& parentPathURL)
{
    CStringA errorResult;
    SVNPool pool;

    /* If the URL is already absolute, there is nothing to do. */

    const char *canonicalized_url = svn_uri_canonicalize (URL, pool);
    if (svn_path_is_url (canonicalized_url))
        return canonicalized_url;

    /* Parse the parent directory URL into its parts. */

    apr_uri_t parent_dir_parsed_uri;
    if (apr_uri_parse (pool, parentPathURL, &parent_dir_parsed_uri))
        return errorResult;

    /* If the parent directory URL is at the server root, then the URL
       may have no / after the hostname so apr_uri_parse() will leave
       the URL's path as NULL. */

    if (! parent_dir_parsed_uri.path)
        parent_dir_parsed_uri.path = apr_pstrmemdup (pool, "/", 1);

    /* Handle URLs relative to the current directory or to the
       repository root.  The backpaths may only remove path elements,
       not the hostname.  This allows an external to refer to another
       repository in the same server relative to the location of this
       repository, say using SVNParentPath. */

    if ((0 == strncmp("../", URL, 3)) || (0 == strncmp("^/", URL, 2)))
    {
        apr_array_header_t *base_components = NULL;
        apr_array_header_t *relative_components = NULL;

        /* Decompose either the parent directory's URL path or the
           repository root's URL path into components.  */

        if (0 == strncmp ("../", URL, 3))
        {
            base_components
                = svn_path_decompose (parent_dir_parsed_uri.path, pool);
            relative_components
                = svn_path_decompose (canonicalized_url, pool);
        }
        else
        {
            apr_uri_t repos_root_parsed_uri;
            if (apr_uri_parse(pool, repositoryRootURL, &repos_root_parsed_uri))
                return errorResult;

            /* If the repository root URL is at the server root, then
               the URL may have no / after the hostname so
               apr_uri_parse() will leave the URL's path as NULL. */

            if (! repos_root_parsed_uri.path)
                repos_root_parsed_uri.path = apr_pstrmemdup (pool, "/", 1);

            base_components
                = svn_path_decompose (repos_root_parsed_uri.path, pool);
            relative_components
                = svn_path_decompose (canonicalized_url + 2, pool);
        }

        for (int i = 0; i < relative_components->nelts; ++i)
        {
            const char *component
                = APR_ARRAY_IDX(relative_components, i, const char *);

            if (0 == strcmp("..", component))
            {
                /* Constructing the final absolute URL together with
                   apr_uri_unparse() requires that the path be absolute,
                   so only pop a component if the component being popped
                   is not the component for the root directory. */

                if (base_components->nelts > 1)
                    apr_array_pop (base_components);
            }
            else
                APR_ARRAY_PUSH (base_components, const char *) = component;
        }

        parent_dir_parsed_uri.path = (char *)svn_path_compose(base_components,
                                                            pool);
        parent_dir_parsed_uri.query = NULL;
        parent_dir_parsed_uri.fragment = NULL;

        return apr_uri_unparse (pool, &parent_dir_parsed_uri, 0);
    }
예제 #5
0
/* Add PATH to the operations tree rooted at OPERATION, creating any
   intermediate nodes that are required.  Here's what's expected for
   each action type:

      ACTION          URL    REV      SRC-FILE  PROPNAME
      ------------    -----  -------  --------  --------
      ACTION_MKDIR    NULL   invalid  NULL      NULL
      ACTION_CP       valid  valid    NULL      NULL
      ACTION_PUT      NULL   invalid  valid     NULL
      ACTION_RM       NULL   invalid  NULL      NULL
      ACTION_PROPSET  valid  invalid  NULL      valid
      ACTION_PROPDEL  valid  invalid  NULL      valid

   Node type information is obtained for any copy source (to determine
   whether to create a file or directory) and for any deleted path (to
   ensure it exists since svn_delta_editor_t->delete_entry doesn't
   return an error on non-existent nodes). */
static svn_error_t *
build(action_code_t action,
      const char *path,
      const char *url,
      svn_revnum_t rev,
      const char *prop_name,
      const char *prop_value,
      const char *src_file,
      svn_revnum_t head,
      const char *anchor,
      svn_ra_session_t *session,
      struct operation *operation,
      apr_pool_t *pool)
{
  apr_array_header_t *path_bits = svn_path_decompose(path, pool);
  const char *path_so_far = "";
  const char *copy_src = NULL;
  svn_revnum_t copy_rev = SVN_INVALID_REVNUM;
  int i;

  /* Look for any previous operations we've recognized for PATH.  If
     any of PATH's ancestors have not yet been traversed, we'll be
     creating OP_OPEN operations for them as we walk down PATH's path
     components. */
  for (i = 0; i < path_bits->nelts; ++i)
    {
      const char *path_bit = APR_ARRAY_IDX(path_bits, i, const char *);
      path_so_far = svn_path_join(path_so_far, path_bit, pool);
      operation = get_operation(path_so_far, operation, pool);

      /* If we cross a replace- or add-with-history, remember the
      source of those things in case we need to lookup the node kind
      of one of their children.  And if this isn't such a copy,
      but we've already seen one in of our parent paths, we just need
      to extend that copy source path by our current path
      component. */
      if (operation->url
          && SVN_IS_VALID_REVNUM(operation->rev)
          && (operation->operation == OP_REPLACE
              || operation->operation == OP_ADD))
        {
          copy_src = subtract_anchor(anchor, operation->url, pool);
          copy_rev = operation->rev;
        }
      else if (copy_src)
        {
          copy_src = svn_path_join(copy_src, path_bit, pool);
        }
    }

  /* Handle property changes. */
  if (prop_name)
    {
      if (operation->operation == OP_DELETE)
        return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                 "cannot set properties on a location being"
                                 " deleted ('%s')", path);
      SVN_ERR(svn_ra_check_path(session,
                                copy_src ? copy_src : path,
                                copy_src ? copy_rev : head,
                                &operation->kind, pool));
      if (operation->kind == svn_node_none)
        return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                 "propset: '%s' not found", path);
      else if ((operation->kind == svn_node_file)
               && (operation->operation == OP_OPEN))
        operation->operation = OP_PROPSET;
      apr_table_set(operation->props, prop_name, prop_value);
      if (!operation->rev)
        operation->rev = rev;
      return SVN_NO_ERROR;
    }

  /* We won't fuss about multiple operations on the same path in the
     following cases:

       - the prior operation was, in fact, a no-op (open)
       - the prior operation was a propset placeholder
       - the prior operation was a deletion

     Note: while the operation structure certainly supports the
     ability to do a copy of a file followed by a put of new contents
     for the file, we don't let that happen (yet).
  */
  if (operation->operation != OP_OPEN
      && operation->operation != OP_PROPSET
      && operation->operation != OP_DELETE)
    return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                             "unsupported multiple operations on '%s'", path);

  /* For deletions, we validate that there's actually something to
     delete.  If this is a deletion of the child of a copied
     directory, we need to remember to look in the copy source tree to
     verify that this thing actually exists. */
  if (action == ACTION_RM)
    {
      operation->operation = OP_DELETE;
      SVN_ERR(svn_ra_check_path(session,
                                copy_src ? copy_src : path,
                                copy_src ? copy_rev : head,
                                &operation->kind, pool));
      if (operation->kind == svn_node_none)
        {
          if (copy_src && strcmp(path, copy_src))
            return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                     "'%s' (from '%s:%ld') not found",
                                     path, copy_src, copy_rev);
          else
            return svn_error_createf(SVN_ERR_BAD_URL, NULL, "'%s' not found",
                                     path);
        }
    }
  /* Handle copy operations (which can be adds or replacements). */
  else if (action == ACTION_CP)
    {
      if (rev > head)
        return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
                                "Copy source revision cannot be younger "
                                "than base revision");
      operation->operation =
        operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
      if (operation->operation == OP_ADD)
        {
          /* There is a bug in the current version of mod_dav_svn
             which incorrectly replaces existing directories.
             Therefore we need to check if the target exists
             and raise an error here. */
          SVN_ERR(svn_ra_check_path(session,
                                    copy_src ? copy_src : path,
                                    copy_src ? copy_rev : head,
                                    &operation->kind, pool));
          if (operation->kind != svn_node_none)
            {
              if (copy_src && strcmp(path, copy_src))
                return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                         "'%s' (from '%s:%ld') already exists",
                                         path, copy_src, copy_rev);
              else
                return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                         "'%s' already exists", path);
            }
        }
      SVN_ERR(svn_ra_check_path(session, subtract_anchor(anchor, url, pool),
                                rev, &operation->kind, pool));
      if (operation->kind == svn_node_none)
        return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                 "'%s' not found",
                                  subtract_anchor(anchor, url, pool));
      operation->url = url;
      operation->rev = rev;
    }
  /* Handle mkdir operations (which can be adds or replacements). */
  else if (action == ACTION_MKDIR)
    {
      operation->operation =
        operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
      operation->kind = svn_node_dir;
    }
  /* Handle put operations (which can be adds, replacements, or opens). */
  else if (action == ACTION_PUT)
    {
      if (operation->operation == OP_DELETE)
        {
          operation->operation = OP_REPLACE;
        }
      else
        {
          SVN_ERR(svn_ra_check_path(session,
                                    copy_src ? copy_src : path,
                                    copy_src ? copy_rev : head,
                                    &operation->kind, pool));
          if (operation->kind == svn_node_file)
            operation->operation = OP_OPEN;
          else if (operation->kind == svn_node_none)
            operation->operation = OP_ADD;
          else
            return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                     "'%s' is not a file", path);
        }
      operation->kind = svn_node_file;
      operation->src_file = src_file;
    }
  else
    {
      /* We shouldn't get here. */
      abort();
    }

  return SVN_NO_ERROR;
}
예제 #6
0
static svn_error_t *
resolve_relative_external_url(const char **resolved_url,
                              const svn_wc_external_item2_t *item,
                              const char *repos_root_url,
                              const char *parent_dir_url,
                              apr_pool_t *result_pool,
                              apr_pool_t *scratch_pool)
{
  const char *url = item->url;
  apr_uri_t parent_dir_uri;
  apr_status_t status;

  *resolved_url = item->url;

  /* If the URL is already absolute, there is nothing to do. */
  if (svn_path_is_url(url))
    {
      /* "http://server/path" */
      *resolved_url = svn_uri_canonicalize(url, result_pool);
      return SVN_NO_ERROR;
    }

  if (url[0] == '/')
    {
      /* "/path", "//path", and "///path" */
      int num_leading_slashes = 1;
      if (url[1] == '/')
        {
          num_leading_slashes++;
          if (url[2] == '/')
            num_leading_slashes++;
        }

      /* "//schema-relative" and in some cases "///schema-relative".
         This last format is supported on file:// schema relative. */
      url = apr_pstrcat(scratch_pool,
                        apr_pstrndup(scratch_pool, url, num_leading_slashes),
                        svn_relpath_canonicalize(url + num_leading_slashes,
                                                 scratch_pool),
                        (char*)NULL);
    }
  else
    {
      /* "^/path" and "../path" */
      url = svn_relpath_canonicalize(url, scratch_pool);
    }

  /* Parse the parent directory URL into its parts. */
  status = apr_uri_parse(scratch_pool, parent_dir_url, &parent_dir_uri);
  if (status)
    return svn_error_createf(SVN_ERR_BAD_URL, 0,
                             "Illegal parent directory URL '%s'",
                             parent_dir_url);

  /* If the parent directory URL is at the server root, then the URL
     may have no / after the hostname so apr_uri_parse() will leave
     the URL's path as NULL. */
  if (! parent_dir_uri.path)
    parent_dir_uri.path = apr_pstrmemdup(scratch_pool, "/", 1);
  parent_dir_uri.query = NULL;
  parent_dir_uri.fragment = NULL;

  /* Handle URLs relative to the current directory or to the
     repository root.  The backpaths may only remove path elements,
     not the hostname.  This allows an external to refer to another
     repository in the same server relative to the location of this
     repository, say using SVNParentPath. */
  if ((0 == strncmp("../", url, 3)) ||
      (0 == strncmp("^/", url, 2)))
    {
      apr_array_header_t *base_components;
      apr_array_header_t *relative_components;
      int i;

      /* Decompose either the parent directory's URL path or the
         repository root's URL path into components.  */
      if (0 == strncmp("../", url, 3))
        {
          base_components = svn_path_decompose(parent_dir_uri.path,
                                               scratch_pool);
          relative_components = svn_path_decompose(url, scratch_pool);
        }
      else
        {
          apr_uri_t repos_root_uri;

          status = apr_uri_parse(scratch_pool, repos_root_url,
                                 &repos_root_uri);
          if (status)
            return svn_error_createf(SVN_ERR_BAD_URL, 0,
                                     "Illegal repository root URL '%s'",
                                     repos_root_url);

          /* If the repository root URL is at the server root, then
             the URL may have no / after the hostname so
             apr_uri_parse() will leave the URL's path as NULL. */
          if (! repos_root_uri.path)
            repos_root_uri.path = apr_pstrmemdup(scratch_pool, "/", 1);

          base_components = svn_path_decompose(repos_root_uri.path,
                                               scratch_pool);
          relative_components = svn_path_decompose(url + 2, scratch_pool);
        }

      for (i = 0; i < relative_components->nelts; ++i)
        {
          const char *component = APR_ARRAY_IDX(relative_components,
                                                i,
                                                const char *);
          if (0 == strcmp("..", component))
            {
              /* Constructing the final absolute URL together with
                 apr_uri_unparse() requires that the path be absolute,
                 so only pop a component if the component being popped
                 is not the component for the root directory. */
              if (base_components->nelts > 1)
                apr_array_pop(base_components);
            }
          else
            APR_ARRAY_PUSH(base_components, const char *) = component;
        }

      parent_dir_uri.path = (char *)svn_path_compose(base_components,
                                                     scratch_pool);
      *resolved_url = svn_uri_canonicalize(apr_uri_unparse(scratch_pool,
                                                           &parent_dir_uri, 0),
                                       result_pool);
      return SVN_NO_ERROR;
    }