1251881Speter/*
2251881Speter * iprops.c:  wrappers around wc inherited property functionality
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter/* ==================================================================== */
25251881Speter
26251881Speter
27251881Speter
28251881Speter/*** Includes. ***/
29251881Speter
30251881Speter#include "svn_error.h"
31251881Speter#include "svn_hash.h"
32251881Speter#include "svn_pools.h"
33251881Speter#include "svn_wc.h"
34251881Speter#include "svn_ra.h"
35251881Speter#include "svn_props.h"
36251881Speter#include "svn_path.h"
37251881Speter
38251881Speter#include "client.h"
39251881Speter#include "svn_private_config.h"
40251881Speter
41251881Speter#include "private/svn_wc_private.h"
42251881Speter
43251881Speter
44251881Speter/*** Code. ***/
45251881Speter
46251881Speter/* Determine if LOCAL_ABSPATH needs an inherited property cache.  If it does,
47251881Speter   then set *NEEDS_CACHE to TRUE, set it to FALSE otherwise.  All other args
48251881Speter   are as per svn_client__get_inheritable_props(). */
49251881Speterstatic svn_error_t *
50251881Speterneed_to_cache_iprops(svn_boolean_t *needs_cache,
51251881Speter                     const char *local_abspath,
52251881Speter                     svn_ra_session_t *ra_session,
53251881Speter                     svn_client_ctx_t *ctx,
54251881Speter                     apr_pool_t *scratch_pool)
55251881Speter{
56251881Speter  svn_boolean_t is_wc_root;
57251881Speter  svn_boolean_t is_switched;
58251881Speter  svn_error_t *err;
59251881Speter
60251881Speter  err = svn_wc_check_root(&is_wc_root, &is_switched, NULL,
61251881Speter                          ctx->wc_ctx, local_abspath,
62251881Speter                           scratch_pool);
63251881Speter
64251881Speter  /* LOCAL_ABSPATH doesn't need a cache if it doesn't exist. */
65251881Speter  if (err)
66251881Speter    {
67251881Speter      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
68251881Speter        {
69251881Speter          svn_error_clear(err);
70251881Speter          is_wc_root = FALSE;
71251881Speter          is_switched = FALSE;
72251881Speter        }
73251881Speter      else
74251881Speter        {
75251881Speter          return svn_error_trace(err);
76251881Speter        }
77251881Speter    }
78251881Speter
79251881Speter  /* Starting assumption. */
80251881Speter  *needs_cache = FALSE;
81251881Speter
82251881Speter  if (is_wc_root || is_switched)
83251881Speter    {
84251881Speter      const char *session_url;
85251881Speter      const char *session_root_url;
86251881Speter
87251881Speter      /* Looks likely that we need an inherited properties cache...Unless
88251881Speter         LOCAL_ABSPATH is a WC root that points to the repos root.  Then it
89251881Speter         doesn't need a cache because it has nowhere to inherit from.  Check
90251881Speter         for that case. */
91251881Speter      SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
92251881Speter      SVN_ERR(svn_ra_get_repos_root2(ra_session, &session_root_url,
93251881Speter                                     scratch_pool));
94251881Speter
95251881Speter      if (strcmp(session_root_url, session_url) != 0)
96251881Speter        *needs_cache = TRUE;
97251881Speter    }
98251881Speter
99251881Speter  return SVN_NO_ERROR;
100251881Speter}
101251881Speter
102251881Spetersvn_error_t *
103251881Spetersvn_client__iprop_relpaths_to_urls(apr_array_header_t *inherited_props,
104251881Speter                                   const char *repos_root_url,
105251881Speter                                   apr_pool_t *result_pool,
106251881Speter                                   apr_pool_t *scratch_pool)
107251881Speter{
108251881Speter  int i;
109251881Speter
110251881Speter  for (i = 0; i < inherited_props->nelts; i++)
111251881Speter    {
112251881Speter      svn_prop_inherited_item_t *elt =
113251881Speter        APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
114251881Speter
115251881Speter      /* Convert repos root relpaths to full URLs. */
116251881Speter      if (! (svn_path_is_url(elt->path_or_url)
117251881Speter             || svn_dirent_is_absolute(elt->path_or_url)))
118251881Speter        {
119251881Speter          elt->path_or_url = svn_path_url_add_component2(repos_root_url,
120251881Speter                                                         elt->path_or_url,
121251881Speter                                                         result_pool);
122251881Speter        }
123251881Speter    }
124251881Speter  return SVN_NO_ERROR;
125251881Speter}
126251881Speter
127251881Speter/* The real implementation of svn_client__get_inheritable_props */
128251881Speterstatic svn_error_t *
129251881Speterget_inheritable_props(apr_hash_t **wcroot_iprops,
130251881Speter                      const char *local_abspath,
131251881Speter                      svn_revnum_t revision,
132251881Speter                      svn_depth_t depth,
133251881Speter                      svn_ra_session_t *ra_session,
134251881Speter                      svn_client_ctx_t *ctx,
135251881Speter                      apr_pool_t *result_pool,
136251881Speter                      apr_pool_t *scratch_pool)
137251881Speter{
138251881Speter  apr_hash_t *iprop_paths;
139251881Speter  apr_hash_index_t *hi;
140251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
141251881Speter  apr_pool_t *session_pool = NULL;
142251881Speter  *wcroot_iprops = apr_hash_make(result_pool);
143251881Speter
144251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
145251881Speter
146251881Speter  /* If we don't have a base revision for LOCAL_ABSPATH then it can't
147251881Speter     possibly be a working copy root, nor can it contain any WC roots
148251881Speter     in the form of switched subtrees.  So there is nothing to cache. */
149251881Speter
150251881Speter  SVN_ERR(svn_wc__get_cached_iprop_children(&iprop_paths, depth,
151251881Speter                                            ctx->wc_ctx, local_abspath,
152251881Speter                                            scratch_pool, iterpool));
153251881Speter
154251881Speter  /* If we are in the midst of a checkout or an update that is bringing in
155251881Speter     an external, then svn_wc__get_cached_iprop_children won't return
156251881Speter     LOCAL_ABSPATH in IPROPS_PATHS because the former has no cached iprops
157251881Speter     yet.  So make sure LOCAL_ABSPATH is present if it's a WC root. */
158251881Speter  if (!svn_hash_gets(iprop_paths, local_abspath))
159251881Speter    {
160251881Speter      svn_boolean_t needs_cached_iprops;
161251881Speter
162251881Speter      SVN_ERR(need_to_cache_iprops(&needs_cached_iprops, local_abspath,
163251881Speter                                   ra_session, ctx, iterpool));
164251881Speter      if (needs_cached_iprops)
165251881Speter        {
166251881Speter          const char *target_abspath = apr_pstrdup(scratch_pool,
167251881Speter                                                   local_abspath);
168251881Speter
169251881Speter          /* As value we set TARGET_ABSPATH, but any string besides ""
170251881Speter             would do */
171251881Speter          svn_hash_sets(iprop_paths, target_abspath, target_abspath);
172251881Speter        }
173251881Speter    }
174251881Speter
175251881Speter      for (hi = apr_hash_first(scratch_pool, iprop_paths);
176251881Speter           hi;
177251881Speter           hi = apr_hash_next(hi))
178251881Speter        {
179289180Speter          const char *child_abspath = apr_hash_this_key(hi);
180289180Speter          const char *child_repos_relpath = apr_hash_this_val(hi);
181251881Speter          const char *url;
182251881Speter          apr_array_header_t *inherited_props;
183251881Speter          svn_error_t *err;
184251881Speter
185251881Speter          svn_pool_clear(iterpool);
186251881Speter
187251881Speter          if (*child_repos_relpath == '\0')
188251881Speter            {
189251881Speter              /* A repository root doesn't have inherited properties */
190251881Speter              continue;
191251881Speter            }
192251881Speter
193251881Speter          SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child_abspath,
194251881Speter                                       iterpool, iterpool));
195251881Speter          if (ra_session)
196251881Speter            SVN_ERR(svn_ra_reparent(ra_session, url, scratch_pool));
197251881Speter          else
198251881Speter            {
199251881Speter              if (! session_pool)
200251881Speter                session_pool = svn_pool_create(scratch_pool);
201251881Speter
202251881Speter              SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
203251881Speter                                                  ctx,
204251881Speter                                                  session_pool, iterpool));
205251881Speter            }
206251881Speter
207251881Speter          err = svn_ra_get_inherited_props(ra_session, &inherited_props,
208251881Speter                                           "", revision,
209251881Speter                                           result_pool, iterpool);
210251881Speter
211251881Speter          if (err)
212251881Speter            {
213251881Speter              if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
214251881Speter                return svn_error_trace(err);
215251881Speter
216251881Speter              svn_error_clear(err);
217251881Speter              continue;
218251881Speter            }
219251881Speter
220251881Speter          svn_hash_sets(*wcroot_iprops,
221251881Speter                        apr_pstrdup(result_pool, child_abspath),
222251881Speter                        inherited_props);
223251881Speter        }
224251881Speter
225251881Speter
226251881Speter  svn_pool_destroy(iterpool);
227251881Speter  if (session_pool)
228251881Speter    svn_pool_destroy(session_pool);
229251881Speter
230251881Speter  return SVN_NO_ERROR;
231251881Speter
232251881Speter}
233251881Speter
234251881Spetersvn_error_t *
235251881Spetersvn_client__get_inheritable_props(apr_hash_t **wcroot_iprops,
236251881Speter                                  const char *local_abspath,
237251881Speter                                  svn_revnum_t revision,
238251881Speter                                  svn_depth_t depth,
239251881Speter                                  svn_ra_session_t *ra_session,
240251881Speter                                  svn_client_ctx_t *ctx,
241251881Speter                                  apr_pool_t *result_pool,
242251881Speter                                  apr_pool_t *scratch_pool)
243251881Speter{
244251881Speter  const char *old_session_url;
245251881Speter  svn_error_t *err;
246251881Speter
247289180Speter  *wcroot_iprops = NULL;
248289180Speter
249251881Speter  if (!SVN_IS_VALID_REVNUM(revision))
250251881Speter    return SVN_NO_ERROR;
251251881Speter
252251881Speter  if (ra_session)
253251881Speter    SVN_ERR(svn_ra_get_session_url(ra_session, &old_session_url, scratch_pool));
254251881Speter
255251881Speter  /* We just wrap a simple helper function, as it is to easy to leave the ra
256251881Speter     session rooted at some wrong path without a wrapper like this.
257251881Speter
258251881Speter     During development we had problems where some now deleted switched path
259251881Speter     made the update try to update to that url instead of the intended url
260251881Speter   */
261251881Speter
262251881Speter  err = get_inheritable_props(wcroot_iprops, local_abspath, revision, depth,
263251881Speter                              ra_session, ctx, result_pool, scratch_pool);
264251881Speter
265251881Speter  if (ra_session)
266251881Speter    {
267251881Speter      err = svn_error_compose_create(
268251881Speter                err,
269251881Speter                svn_ra_reparent(ra_session, old_session_url, scratch_pool));
270251881Speter    }
271251881Speter  return svn_error_trace(err);
272251881Speter}
273