iprops.c revision 299742
1/*
2 * iprops.c:  wrappers around wc inherited property functionality
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24/* ==================================================================== */
25
26
27
28/*** Includes. ***/
29
30#include "svn_error.h"
31#include "svn_hash.h"
32#include "svn_pools.h"
33#include "svn_wc.h"
34#include "svn_ra.h"
35#include "svn_props.h"
36#include "svn_path.h"
37
38#include "client.h"
39#include "svn_private_config.h"
40
41#include "private/svn_wc_private.h"
42
43
44/*** Code. ***/
45
46/* Determine if LOCAL_ABSPATH needs an inherited property cache.  If it does,
47   then set *NEEDS_CACHE to TRUE, set it to FALSE otherwise.  All other args
48   are as per svn_client__get_inheritable_props(). */
49static svn_error_t *
50need_to_cache_iprops(svn_boolean_t *needs_cache,
51                     const char *local_abspath,
52                     svn_ra_session_t *ra_session,
53                     svn_client_ctx_t *ctx,
54                     apr_pool_t *scratch_pool)
55{
56  svn_boolean_t is_wc_root;
57  svn_boolean_t is_switched;
58  svn_error_t *err;
59
60  err = svn_wc_check_root(&is_wc_root, &is_switched, NULL,
61                          ctx->wc_ctx, local_abspath,
62                           scratch_pool);
63
64  /* LOCAL_ABSPATH doesn't need a cache if it doesn't exist. */
65  if (err)
66    {
67      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
68        {
69          svn_error_clear(err);
70          is_wc_root = FALSE;
71          is_switched = FALSE;
72        }
73      else
74        {
75          return svn_error_trace(err);
76        }
77    }
78
79  /* Starting assumption. */
80  *needs_cache = FALSE;
81
82  if (is_wc_root || is_switched)
83    {
84      const char *session_url;
85      const char *session_root_url;
86
87      /* Looks likely that we need an inherited properties cache...Unless
88         LOCAL_ABSPATH is a WC root that points to the repos root.  Then it
89         doesn't need a cache because it has nowhere to inherit from.  Check
90         for that case. */
91      SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
92      SVN_ERR(svn_ra_get_repos_root2(ra_session, &session_root_url,
93                                     scratch_pool));
94
95      if (strcmp(session_root_url, session_url) != 0)
96        *needs_cache = TRUE;
97    }
98
99  return SVN_NO_ERROR;
100}
101
102svn_error_t *
103svn_client__iprop_relpaths_to_urls(apr_array_header_t *inherited_props,
104                                   const char *repos_root_url,
105                                   apr_pool_t *result_pool,
106                                   apr_pool_t *scratch_pool)
107{
108  int i;
109
110  for (i = 0; i < inherited_props->nelts; i++)
111    {
112      svn_prop_inherited_item_t *elt =
113        APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
114
115      /* Convert repos root relpaths to full URLs. */
116      if (! (svn_path_is_url(elt->path_or_url)
117             || svn_dirent_is_absolute(elt->path_or_url)))
118        {
119          elt->path_or_url = svn_path_url_add_component2(repos_root_url,
120                                                         elt->path_or_url,
121                                                         result_pool);
122        }
123    }
124  return SVN_NO_ERROR;
125}
126
127/* The real implementation of svn_client__get_inheritable_props */
128static svn_error_t *
129get_inheritable_props(apr_hash_t **wcroot_iprops,
130                      const char *local_abspath,
131                      svn_revnum_t revision,
132                      svn_depth_t depth,
133                      svn_ra_session_t *ra_session,
134                      svn_client_ctx_t *ctx,
135                      apr_pool_t *result_pool,
136                      apr_pool_t *scratch_pool)
137{
138  apr_hash_t *iprop_paths;
139  apr_hash_index_t *hi;
140  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
141  apr_pool_t *session_pool = NULL;
142  *wcroot_iprops = apr_hash_make(result_pool);
143
144  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
145
146  /* If we don't have a base revision for LOCAL_ABSPATH then it can't
147     possibly be a working copy root, nor can it contain any WC roots
148     in the form of switched subtrees.  So there is nothing to cache. */
149
150  SVN_ERR(svn_wc__get_cached_iprop_children(&iprop_paths, depth,
151                                            ctx->wc_ctx, local_abspath,
152                                            scratch_pool, iterpool));
153
154  /* If we are in the midst of a checkout or an update that is bringing in
155     an external, then svn_wc__get_cached_iprop_children won't return
156     LOCAL_ABSPATH in IPROPS_PATHS because the former has no cached iprops
157     yet.  So make sure LOCAL_ABSPATH is present if it's a WC root. */
158  if (!svn_hash_gets(iprop_paths, local_abspath))
159    {
160      svn_boolean_t needs_cached_iprops;
161
162      SVN_ERR(need_to_cache_iprops(&needs_cached_iprops, local_abspath,
163                                   ra_session, ctx, iterpool));
164      if (needs_cached_iprops)
165        {
166          const char *target_abspath = apr_pstrdup(scratch_pool,
167                                                   local_abspath);
168
169          /* As value we set TARGET_ABSPATH, but any string besides ""
170             would do */
171          svn_hash_sets(iprop_paths, target_abspath, target_abspath);
172        }
173    }
174
175      for (hi = apr_hash_first(scratch_pool, iprop_paths);
176           hi;
177           hi = apr_hash_next(hi))
178        {
179          const char *child_abspath = apr_hash_this_key(hi);
180          const char *child_repos_relpath = apr_hash_this_val(hi);
181          const char *url;
182          apr_array_header_t *inherited_props;
183          svn_error_t *err;
184
185          svn_pool_clear(iterpool);
186
187          if (*child_repos_relpath == '\0')
188            {
189              /* A repository root doesn't have inherited properties */
190              continue;
191            }
192
193          SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child_abspath,
194                                       iterpool, iterpool));
195          if (ra_session)
196            SVN_ERR(svn_ra_reparent(ra_session, url, scratch_pool));
197          else
198            {
199              if (! session_pool)
200                session_pool = svn_pool_create(scratch_pool);
201
202              SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
203                                                  ctx,
204                                                  session_pool, iterpool));
205            }
206
207          err = svn_ra_get_inherited_props(ra_session, &inherited_props,
208                                           "", revision,
209                                           result_pool, iterpool);
210
211          if (err)
212            {
213              if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
214                return svn_error_trace(err);
215
216              svn_error_clear(err);
217              continue;
218            }
219
220          svn_hash_sets(*wcroot_iprops,
221                        apr_pstrdup(result_pool, child_abspath),
222                        inherited_props);
223        }
224
225
226  svn_pool_destroy(iterpool);
227  if (session_pool)
228    svn_pool_destroy(session_pool);
229
230  return SVN_NO_ERROR;
231
232}
233
234svn_error_t *
235svn_client__get_inheritable_props(apr_hash_t **wcroot_iprops,
236                                  const char *local_abspath,
237                                  svn_revnum_t revision,
238                                  svn_depth_t depth,
239                                  svn_ra_session_t *ra_session,
240                                  svn_client_ctx_t *ctx,
241                                  apr_pool_t *result_pool,
242                                  apr_pool_t *scratch_pool)
243{
244  const char *old_session_url;
245  svn_error_t *err;
246
247  *wcroot_iprops = NULL;
248
249  if (!SVN_IS_VALID_REVNUM(revision))
250    return SVN_NO_ERROR;
251
252  if (ra_session)
253    SVN_ERR(svn_ra_get_session_url(ra_session, &old_session_url, scratch_pool));
254
255  /* We just wrap a simple helper function, as it is to easy to leave the ra
256     session rooted at some wrong path without a wrapper like this.
257
258     During development we had problems where some now deleted switched path
259     made the update try to update to that url instead of the intended url
260   */
261
262  err = get_inheritable_props(wcroot_iprops, local_abspath, revision, depth,
263                              ra_session, ctx, result_pool, scratch_pool);
264
265  if (ra_session)
266    {
267      err = svn_error_compose_create(
268                err,
269                svn_ra_reparent(ra_session, old_session_url, scratch_pool));
270    }
271  return svn_error_trace(err);
272}
273