1251881Speter/*
2251881Speter * upgrade.c:  wrapper around wc upgrade 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_time.h"
31251881Speter#include "svn_wc.h"
32251881Speter#include "svn_client.h"
33251881Speter#include "svn_config.h"
34251881Speter#include "svn_dirent_uri.h"
35251881Speter#include "svn_path.h"
36251881Speter#include "svn_pools.h"
37251881Speter#include "client.h"
38251881Speter#include "svn_props.h"
39251881Speter
40251881Speter#include "svn_private_config.h"
41251881Speter#include "private/svn_wc_private.h"
42251881Speter
43251881Speter
44251881Speter/*** Code. ***/
45251881Speter
46251881Speter/* callback baton for fetch_repos_info */
47251881Speterstruct repos_info_baton
48251881Speter{
49251881Speter  apr_pool_t *state_pool;
50251881Speter  svn_client_ctx_t *ctx;
51251881Speter  const char *last_repos;
52251881Speter  const char *last_uuid;
53251881Speter};
54251881Speter
55251881Speter/* svn_wc_upgrade_get_repos_info_t implementation for calling
56251881Speter   svn_wc_upgrade() from svn_client_upgrade() */
57251881Speterstatic svn_error_t *
58251881Speterfetch_repos_info(const char **repos_root,
59251881Speter                 const char **repos_uuid,
60251881Speter                 void *baton,
61251881Speter                 const char *url,
62251881Speter                 apr_pool_t *result_pool,
63251881Speter                 apr_pool_t *scratch_pool)
64251881Speter{
65251881Speter  struct repos_info_baton *ri = baton;
66251881Speter
67251881Speter  /* The same info is likely to retrieved multiple times (e.g. externals) */
68251881Speter  if (ri->last_repos && svn_uri__is_ancestor(ri->last_repos, url))
69251881Speter    {
70251881Speter      *repos_root = apr_pstrdup(result_pool, ri->last_repos);
71251881Speter      *repos_uuid = apr_pstrdup(result_pool, ri->last_uuid);
72251881Speter      return SVN_NO_ERROR;
73251881Speter    }
74251881Speter
75251881Speter  SVN_ERR(svn_client_get_repos_root(repos_root, repos_uuid, url, ri->ctx,
76251881Speter                                    result_pool, scratch_pool));
77251881Speter
78251881Speter  /* Store data for further calls */
79251881Speter  ri->last_repos = apr_pstrdup(ri->state_pool, *repos_root);
80251881Speter  ri->last_uuid = apr_pstrdup(ri->state_pool, *repos_uuid);
81251881Speter
82251881Speter  return SVN_NO_ERROR;
83251881Speter}
84251881Speter
85286506Speter/* Forward definition. Upgrades svn:externals properties in the working copy
86289180Speter   LOCAL_ABSPATH to the WC-NG  storage. INFO_BATON will be used to fetch
87289180Speter   repository info using fetch_repos_info() function if needed.
88286506Speter */
89286506Speterstatic svn_error_t *
90286506Speterupgrade_externals_from_properties(svn_client_ctx_t *ctx,
91286506Speter                                  const char *local_abspath,
92289180Speter                                  struct repos_info_baton *info_baton,
93286506Speter                                  apr_pool_t *scratch_pool);
94286506Speter
95251881Spetersvn_error_t *
96251881Spetersvn_client_upgrade(const char *path,
97251881Speter                   svn_client_ctx_t *ctx,
98251881Speter                   apr_pool_t *scratch_pool)
99251881Speter{
100251881Speter  const char *local_abspath;
101251881Speter  apr_hash_t *externals;
102251881Speter  struct repos_info_baton info_baton;
103251881Speter
104251881Speter  info_baton.state_pool = scratch_pool;
105251881Speter  info_baton.ctx = ctx;
106251881Speter  info_baton.last_repos = NULL;
107251881Speter  info_baton.last_uuid = NULL;
108251881Speter
109251881Speter  if (svn_path_is_url(path))
110251881Speter    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
111251881Speter                             _("'%s' is not a local path"), path);
112251881Speter
113251881Speter  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
114251881Speter  SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath,
115251881Speter                         fetch_repos_info, &info_baton,
116251881Speter                         ctx->cancel_func, ctx->cancel_baton,
117251881Speter                         ctx->notify_func2, ctx->notify_baton2,
118251881Speter                         scratch_pool));
119251881Speter
120286506Speter  SVN_ERR(svn_wc__externals_defined_below(&externals,
121286506Speter                                          ctx->wc_ctx, local_abspath,
122286506Speter                                          scratch_pool, scratch_pool));
123286506Speter
124286506Speter  if (apr_hash_count(externals) > 0)
125286506Speter    {
126286506Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
127286506Speter      apr_hash_index_t *hi;
128286506Speter
129286506Speter      /* We are upgrading from >= 1.7. No need to upgrade from
130286506Speter         svn:externals properties. And by that avoiding the removal
131286506Speter         of recorded externals information (issue #4519)
132286506Speter
133286506Speter         Only directory externals need an explicit upgrade */
134286506Speter      for (hi = apr_hash_first(scratch_pool, externals);
135286506Speter           hi;
136286506Speter           hi = apr_hash_next(hi))
137286506Speter        {
138286506Speter          const char *ext_abspath;
139286506Speter          svn_node_kind_t kind;
140286506Speter
141286506Speter          svn_pool_clear(iterpool);
142286506Speter
143289180Speter          ext_abspath = apr_hash_this_key(hi);
144286506Speter
145286506Speter          SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL,
146286506Speter                                             ctx->wc_ctx, local_abspath,
147286506Speter                                             ext_abspath, FALSE,
148286506Speter                                             iterpool, iterpool));
149286506Speter
150286506Speter          if (kind == svn_node_dir)
151286506Speter            {
152286506Speter              svn_error_t *err = svn_client_upgrade(ext_abspath, ctx, iterpool);
153286506Speter
154286506Speter              if (err)
155286506Speter                {
156286506Speter                  svn_wc_notify_t *notify =
157286506Speter                            svn_wc_create_notify(ext_abspath,
158286506Speter                                                 svn_wc_notify_failed_external,
159286506Speter                                                 iterpool);
160286506Speter                  notify->err = err;
161286506Speter                  ctx->notify_func2(ctx->notify_baton2,
162286506Speter                                    notify, iterpool);
163286506Speter                  svn_error_clear(err);
164286506Speter                  /* Next external node, please... */
165286506Speter                }
166286506Speter            }
167286506Speter        }
168286506Speter
169286506Speter      svn_pool_destroy(iterpool);
170286506Speter    }
171286506Speter  else
172286506Speter    {
173286506Speter      /* Upgrading from <= 1.6, or no svn:properties defined.
174286506Speter         (There is no way to detect the difference from libsvn_client :( ) */
175286506Speter
176286506Speter      SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath,
177289180Speter                                                &info_baton, scratch_pool));
178286506Speter    }
179286506Speter  return SVN_NO_ERROR;
180286506Speter}
181286506Speter
182362181Sdim/* Helper for upgrade_externals_from_properties: upgrades one external ITEM
183362181Sdim   in EXTERNALS_PARENT. Uses SCRATCH_POOL for temporary allocations. */
184286506Speterstatic svn_error_t *
185362181Sdimupgrade_external_item(svn_client_ctx_t *ctx,
186362181Sdim                      const char *externals_parent_abspath,
187362181Sdim                      const char *externals_parent_url,
188362181Sdim                      const char *externals_parent_repos_root_url,
189362181Sdim                      svn_wc_external_item2_t *item,
190362181Sdim                      struct repos_info_baton *info_baton,
191362181Sdim                      apr_pool_t *scratch_pool)
192362181Sdim{
193362181Sdim  const char *resolved_url;
194362181Sdim  const char *external_abspath;
195362181Sdim  const char *repos_relpath;
196362181Sdim  const char *repos_root_url;
197362181Sdim  const char *repos_uuid;
198362181Sdim  svn_node_kind_t external_kind;
199362181Sdim  svn_revnum_t peg_revision;
200362181Sdim  svn_revnum_t revision;
201362181Sdim  svn_error_t *err;
202362181Sdim
203362181Sdim  external_abspath = svn_dirent_join(externals_parent_abspath,
204362181Sdim                                     item->target_dir,
205362181Sdim                                     scratch_pool);
206362181Sdim
207362181Sdim  SVN_ERR(svn_wc__resolve_relative_external_url(
208362181Sdim              &resolved_url,
209362181Sdim              item,
210362181Sdim              externals_parent_repos_root_url,
211362181Sdim              externals_parent_url,
212362181Sdim              scratch_pool, scratch_pool));
213362181Sdim
214362181Sdim  /* This is a hack. We only need to call svn_wc_upgrade() on external
215362181Sdim   * dirs, as file externals are upgraded along with their defining
216362181Sdim   * WC.  Reading the kind will throw an exception on an external dir,
217362181Sdim   * saying that the wc must be upgraded.  If it's a file, the lookup
218362181Sdim   * is done in an adm_dir belonging to the defining wc (which has
219362181Sdim   * already been upgraded) and no error is returned.  If it doesn't
220362181Sdim   * exist (external that isn't checked out yet), we'll just get
221362181Sdim   * svn_node_none. */
222362181Sdim  err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx,
223362181Sdim                          external_abspath, TRUE, FALSE, scratch_pool);
224362181Sdim  if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
225362181Sdim    {
226362181Sdim      svn_error_clear(err);
227362181Sdim
228362181Sdim      SVN_ERR(svn_client_upgrade(external_abspath, ctx, scratch_pool));
229362181Sdim    }
230362181Sdim  else if (err)
231362181Sdim    return svn_error_trace(err);
232362181Sdim
233362181Sdim  /* The upgrade of any dir should be done now, get the now reliable
234362181Sdim   * kind. */
235362181Sdim  SVN_ERR(svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath,
236362181Sdim                            TRUE, FALSE, scratch_pool));
237362181Sdim
238362181Sdim  /* Update the EXTERNALS table according to the root URL,
239362181Sdim   * relpath and uuid known in the upgraded external WC. */
240362181Sdim
241362181Sdim  /* We should probably have a function that provides all three
242362181Sdim   * of root URL, repos relpath and uuid at once, but here goes... */
243362181Sdim
244362181Sdim  /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND
245362181Sdim   * when the node is not present in the file system.
246362181Sdim   * svn_wc__node_get_repos_info() would try to derive the URL. */
247362181Sdim  SVN_ERR(svn_wc__node_get_repos_info(NULL,
248362181Sdim                                      &repos_relpath,
249362181Sdim                                      &repos_root_url,
250362181Sdim                                      &repos_uuid,
251362181Sdim                                      ctx->wc_ctx,
252362181Sdim                                      external_abspath,
253362181Sdim                                      scratch_pool, scratch_pool));
254362181Sdim
255362181Sdim  /* If we haven't got any information from the checked out external,
256362181Sdim   * or if the URL information mismatches the external's definition,
257362181Sdim   * ask fetch_repos_info() to find out the repos root. */
258362181Sdim  if (0 != strcmp(resolved_url,
259362181Sdim                  svn_path_url_add_component2(repos_root_url,
260362181Sdim                                              repos_relpath,
261362181Sdim                                              scratch_pool)))
262362181Sdim    {
263362181Sdim      SVN_ERR(fetch_repos_info(&repos_root_url, &repos_uuid, info_baton,
264362181Sdim                               resolved_url, scratch_pool, scratch_pool));
265362181Sdim
266362181Sdim      repos_relpath = svn_uri_skip_ancestor(repos_root_url,
267362181Sdim                                            resolved_url,
268362181Sdim                                            scratch_pool);
269362181Sdim
270362181Sdim      /* There's just the URL, no idea what kind the external is.
271362181Sdim       * That's fine, as the external isn't even checked out yet.
272362181Sdim       * The kind will be set during the next 'update'. */
273362181Sdim      external_kind = svn_node_unknown;
274362181Sdim    }
275362181Sdim
276362181Sdim  peg_revision = (item->peg_revision.kind == svn_opt_revision_number
277362181Sdim                     ? item->peg_revision.value.number
278362181Sdim                     : SVN_INVALID_REVNUM);
279362181Sdim
280362181Sdim  revision = (item->revision.kind == svn_opt_revision_number
281362181Sdim                 ? item->revision.value.number
282362181Sdim                 : SVN_INVALID_REVNUM);
283362181Sdim
284362181Sdim  SVN_ERR(svn_wc__upgrade_add_external_info(ctx->wc_ctx,
285362181Sdim                                            external_abspath,
286362181Sdim                                            external_kind,
287362181Sdim                                            externals_parent_abspath,
288362181Sdim                                            repos_relpath,
289362181Sdim                                            repos_root_url,
290362181Sdim                                            repos_uuid,
291362181Sdim                                            peg_revision,
292362181Sdim                                            revision,
293362181Sdim                                            scratch_pool));
294362181Sdim
295362181Sdim  return SVN_NO_ERROR;
296362181Sdim}
297362181Sdim
298362181Sdimstatic svn_error_t *
299286506Speterupgrade_externals_from_properties(svn_client_ctx_t *ctx,
300286506Speter                                  const char *local_abspath,
301289180Speter                                  struct repos_info_baton *info_baton,
302286506Speter                                  apr_pool_t *scratch_pool)
303286506Speter{
304286506Speter  apr_hash_index_t *hi;
305286506Speter  apr_pool_t *iterpool;
306362181Sdim  apr_pool_t *inner_iterpool;
307286506Speter  apr_hash_t *externals;
308286506Speter  svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
309286506Speter
310251881Speter  /* Now it's time to upgrade the externals too. We do it after the wc
311251881Speter     upgrade to avoid that errors in the externals causes the wc upgrade to
312251881Speter     fail. Thanks to caching the performance penalty of walking the wc a
313251881Speter     second time shouldn't be too severe */
314251881Speter  SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS,
315251881Speter                              local_abspath, &rev, &rev, NULL,
316251881Speter                              svn_depth_infinity, NULL, ctx,
317251881Speter                              scratch_pool, scratch_pool));
318251881Speter
319251881Speter  iterpool = svn_pool_create(scratch_pool);
320362181Sdim  inner_iterpool = svn_pool_create(scratch_pool);
321251881Speter
322251881Speter  for (hi = apr_hash_first(scratch_pool, externals); hi;
323251881Speter       hi = apr_hash_next(hi))
324251881Speter    {
325251881Speter      int i;
326251881Speter      const char *externals_parent_url;
327251881Speter      const char *externals_parent_repos_root_url;
328251881Speter      const char *externals_parent_repos_relpath;
329362181Sdim      const char *externals_parent_abspath = apr_hash_this_key(hi);
330289180Speter      svn_string_t *external_desc = apr_hash_this_val(hi);
331251881Speter      apr_array_header_t *externals_p;
332251881Speter      svn_error_t *err;
333251881Speter
334251881Speter      svn_pool_clear(iterpool);
335362181Sdim
336362181Sdim      /* svn_client_propget5() has API promise to return absolute paths. */
337362181Sdim      SVN_ERR_ASSERT(svn_dirent_is_absolute(externals_parent_abspath));
338362181Sdim
339251881Speter      externals_p = apr_array_make(iterpool, 1,
340251881Speter                                   sizeof(svn_wc_external_item2_t*));
341251881Speter
342251881Speter      /* In this loop, an error causes the respective externals definition, or
343251881Speter       * the external (inner loop), to be skipped, so that upgrade carries on
344251881Speter       * with the other externals. */
345362181Sdim      err = svn_wc__node_get_repos_info(NULL,
346362181Sdim                                        &externals_parent_repos_relpath,
347362181Sdim                                        &externals_parent_repos_root_url,
348362181Sdim                                        NULL,
349362181Sdim                                        ctx->wc_ctx,
350362181Sdim                                        externals_parent_abspath,
351362181Sdim                                        iterpool, iterpool);
352251881Speter
353251881Speter      if (!err)
354362181Sdim        {
355362181Sdim          err = svn_wc_parse_externals_description3(
356362181Sdim              &externals_p, svn_dirent_dirname(local_abspath, iterpool),
357362181Sdim              external_desc->data, FALSE, iterpool);
358362181Sdim        }
359251881Speter
360251881Speter      if (err)
361251881Speter        {
362251881Speter          svn_wc_notify_t *notify =
363362181Sdim              svn_wc_create_notify(externals_parent_abspath,
364251881Speter                                   svn_wc_notify_failed_external,
365251881Speter                                   scratch_pool);
366251881Speter          notify->err = err;
367251881Speter
368251881Speter          ctx->notify_func2(ctx->notify_baton2,
369251881Speter                            notify, scratch_pool);
370251881Speter
371251881Speter          svn_error_clear(err);
372251881Speter
373251881Speter          /* Next externals definition, please... */
374251881Speter          continue;
375251881Speter        }
376251881Speter
377362181Sdim      externals_parent_url = svn_path_url_add_component2(
378362181Sdim          externals_parent_repos_root_url,
379362181Sdim          externals_parent_repos_relpath,
380362181Sdim          iterpool);
381362181Sdim
382251881Speter      for (i = 0; i < externals_p->nelts; i++)
383251881Speter        {
384251881Speter          svn_wc_external_item2_t *item;
385251881Speter
386251881Speter          item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
387251881Speter
388362181Sdim          svn_pool_clear(inner_iterpool);
389362181Sdim          err = upgrade_external_item(ctx, externals_parent_abspath,
390362181Sdim                                      externals_parent_url,
391362181Sdim                                      externals_parent_repos_root_url,
392362181Sdim                                      item, info_baton, inner_iterpool);
393251881Speter
394251881Speter          if (err)
395251881Speter            {
396251881Speter              svn_wc_notify_t *notify =
397362181Sdim                  svn_wc_create_notify(svn_dirent_join(externals_parent_abspath,
398362181Sdim                                                       item->target_dir,
399362181Sdim                                                       inner_iterpool),
400251881Speter                                       svn_wc_notify_failed_external,
401251881Speter                                       scratch_pool);
402251881Speter              notify->err = err;
403251881Speter              ctx->notify_func2(ctx->notify_baton2,
404251881Speter                                notify, scratch_pool);
405251881Speter              svn_error_clear(err);
406251881Speter              /* Next external node, please... */
407251881Speter            }
408251881Speter        }
409251881Speter    }
410251881Speter
411362181Sdim  svn_pool_destroy(inner_iterpool);
412251881Speter  svn_pool_destroy(iterpool);
413251881Speter
414251881Speter  return SVN_NO_ERROR;
415251881Speter}
416