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
85251881Spetersvn_error_t *
86251881Spetersvn_client_upgrade(const char *path,
87251881Speter                   svn_client_ctx_t *ctx,
88251881Speter                   apr_pool_t *scratch_pool)
89251881Speter{
90251881Speter  const char *local_abspath;
91251881Speter  apr_hash_t *externals;
92251881Speter  apr_hash_index_t *hi;
93251881Speter  apr_pool_t *iterpool;
94251881Speter  apr_pool_t *iterpool2;
95251881Speter  svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
96251881Speter  struct repos_info_baton info_baton;
97251881Speter
98251881Speter  info_baton.state_pool = scratch_pool;
99251881Speter  info_baton.ctx = ctx;
100251881Speter  info_baton.last_repos = NULL;
101251881Speter  info_baton.last_uuid = NULL;
102251881Speter
103251881Speter  if (svn_path_is_url(path))
104251881Speter    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
105251881Speter                             _("'%s' is not a local path"), path);
106251881Speter
107251881Speter  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
108251881Speter  SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath,
109251881Speter                         fetch_repos_info, &info_baton,
110251881Speter                         ctx->cancel_func, ctx->cancel_baton,
111251881Speter                         ctx->notify_func2, ctx->notify_baton2,
112251881Speter                         scratch_pool));
113251881Speter
114251881Speter  /* Now it's time to upgrade the externals too. We do it after the wc
115251881Speter     upgrade to avoid that errors in the externals causes the wc upgrade to
116251881Speter     fail. Thanks to caching the performance penalty of walking the wc a
117251881Speter     second time shouldn't be too severe */
118251881Speter  SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS,
119251881Speter                              local_abspath, &rev, &rev, NULL,
120251881Speter                              svn_depth_infinity, NULL, ctx,
121251881Speter                              scratch_pool, scratch_pool));
122251881Speter
123251881Speter  iterpool = svn_pool_create(scratch_pool);
124251881Speter  iterpool2 = svn_pool_create(scratch_pool);
125251881Speter
126251881Speter  for (hi = apr_hash_first(scratch_pool, externals); hi;
127251881Speter       hi = apr_hash_next(hi))
128251881Speter    {
129251881Speter      int i;
130251881Speter      const char *externals_parent_abspath;
131251881Speter      const char *externals_parent_url;
132251881Speter      const char *externals_parent_repos_root_url;
133251881Speter      const char *externals_parent_repos_relpath;
134251881Speter      const char *externals_parent = svn__apr_hash_index_key(hi);
135251881Speter      svn_string_t *external_desc = svn__apr_hash_index_val(hi);
136251881Speter      apr_array_header_t *externals_p;
137251881Speter      svn_error_t *err;
138251881Speter
139251881Speter      svn_pool_clear(iterpool);
140251881Speter      externals_p = apr_array_make(iterpool, 1,
141251881Speter                                   sizeof(svn_wc_external_item2_t*));
142251881Speter
143251881Speter      /* In this loop, an error causes the respective externals definition, or
144251881Speter       * the external (inner loop), to be skipped, so that upgrade carries on
145251881Speter       * with the other externals. */
146251881Speter
147251881Speter      err = svn_dirent_get_absolute(&externals_parent_abspath,
148251881Speter                                    externals_parent, iterpool);
149251881Speter
150251881Speter      if (!err)
151251881Speter        err = svn_wc__node_get_repos_info(NULL,
152251881Speter                                          &externals_parent_repos_relpath,
153251881Speter                                          &externals_parent_repos_root_url,
154251881Speter                                          NULL,
155251881Speter                                          ctx->wc_ctx,
156251881Speter                                          externals_parent_abspath,
157251881Speter                                          iterpool, iterpool);
158251881Speter
159251881Speter      if (!err)
160251881Speter        externals_parent_url = svn_path_url_add_component2(
161251881Speter                                    externals_parent_repos_root_url,
162251881Speter                                    externals_parent_repos_relpath,
163251881Speter                                    iterpool);
164251881Speter      if (!err)
165251881Speter        err = svn_wc_parse_externals_description3(
166251881Speter                  &externals_p, svn_dirent_dirname(path, iterpool),
167251881Speter                  external_desc->data, FALSE, iterpool);
168251881Speter      if (err)
169251881Speter        {
170251881Speter          svn_wc_notify_t *notify =
171251881Speter              svn_wc_create_notify(externals_parent,
172251881Speter                                   svn_wc_notify_failed_external,
173251881Speter                                   scratch_pool);
174251881Speter          notify->err = err;
175251881Speter
176251881Speter          ctx->notify_func2(ctx->notify_baton2,
177251881Speter                            notify, scratch_pool);
178251881Speter
179251881Speter          svn_error_clear(err);
180251881Speter
181251881Speter          /* Next externals definition, please... */
182251881Speter          continue;
183251881Speter        }
184251881Speter
185251881Speter      for (i = 0; i < externals_p->nelts; i++)
186251881Speter        {
187251881Speter          svn_wc_external_item2_t *item;
188251881Speter          const char *resolved_url;
189251881Speter          const char *external_abspath;
190251881Speter          const char *repos_relpath;
191251881Speter          const char *repos_root_url;
192251881Speter          const char *repos_uuid;
193251881Speter          svn_node_kind_t external_kind;
194251881Speter          svn_revnum_t peg_revision;
195251881Speter          svn_revnum_t revision;
196251881Speter
197251881Speter          item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
198251881Speter
199251881Speter          svn_pool_clear(iterpool2);
200251881Speter          external_abspath = svn_dirent_join(externals_parent_abspath,
201251881Speter                                             item->target_dir,
202251881Speter                                             iterpool2);
203251881Speter
204251881Speter          err = svn_wc__resolve_relative_external_url(
205251881Speter                                              &resolved_url,
206251881Speter                                              item,
207251881Speter                                              externals_parent_repos_root_url,
208251881Speter                                              externals_parent_url,
209251881Speter                                              scratch_pool, scratch_pool);
210251881Speter          if (err)
211251881Speter            goto handle_error;
212251881Speter
213251881Speter          /* This is a hack. We only need to call svn_wc_upgrade() on external
214251881Speter           * dirs, as file externals are upgraded along with their defining
215251881Speter           * WC.  Reading the kind will throw an exception on an external dir,
216251881Speter           * saying that the wc must be upgraded.  If it's a file, the lookup
217251881Speter           * is done in an adm_dir belonging to the defining wc (which has
218251881Speter           * already been upgraded) and no error is returned.  If it doesn't
219251881Speter           * exist (external that isn't checked out yet), we'll just get
220251881Speter           * svn_node_none. */
221251881Speter          err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx,
222251881Speter                                  external_abspath, TRUE, FALSE, iterpool2);
223251881Speter          if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
224251881Speter            {
225251881Speter              svn_error_clear(err);
226251881Speter
227251881Speter              err = svn_client_upgrade(external_abspath, ctx, iterpool2);
228251881Speter              if (err)
229251881Speter                goto handle_error;
230251881Speter            }
231251881Speter          else if (err)
232251881Speter            goto handle_error;
233251881Speter
234251881Speter          /* The upgrade of any dir should be done now, get the now reliable
235251881Speter           * kind. */
236251881Speter          err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath,
237251881Speter                                  TRUE, FALSE, iterpool2);
238251881Speter          if (err)
239251881Speter            goto handle_error;
240251881Speter
241251881Speter          /* Update the EXTERNALS table according to the root URL,
242251881Speter           * relpath and uuid known in the upgraded external WC. */
243251881Speter
244251881Speter          /* We should probably have a function that provides all three
245251881Speter           * of root URL, repos relpath and uuid at once, but here goes... */
246251881Speter
247251881Speter          /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND
248251881Speter           * when the node is not present in the file system.
249251881Speter           * svn_wc__node_get_repos_info() would try to derive the URL. */
250251881Speter          err = svn_wc__node_get_repos_info(NULL,
251251881Speter                                            &repos_relpath,
252251881Speter                                            &repos_root_url,
253251881Speter                                            &repos_uuid,
254251881Speter                                            ctx->wc_ctx,
255251881Speter                                            external_abspath,
256251881Speter                                            iterpool2, iterpool2);
257251881Speter          if (err)
258251881Speter            goto handle_error;
259251881Speter
260251881Speter          /* If we haven't got any information from the checked out external,
261251881Speter           * or if the URL information mismatches the external's definition,
262251881Speter           * ask fetch_repos_info() to find out the repos root. */
263251881Speter          if (0 != strcmp(resolved_url,
264251881Speter                          svn_path_url_add_component2(repos_root_url,
265251881Speter                                                      repos_relpath,
266251881Speter                                                      scratch_pool)))
267251881Speter            {
268251881Speter              err = fetch_repos_info(&repos_root_url,
269251881Speter                                     &repos_uuid,
270251881Speter                                     &info_baton,
271251881Speter                                     resolved_url,
272251881Speter                                     scratch_pool, scratch_pool);
273251881Speter              if (err)
274251881Speter                goto handle_error;
275251881Speter
276251881Speter              repos_relpath = svn_uri_skip_ancestor(repos_root_url,
277251881Speter                                                    resolved_url,
278251881Speter                                                    iterpool2);
279251881Speter
280251881Speter              /* There's just the URL, no idea what kind the external is.
281251881Speter               * That's fine, as the external isn't even checked out yet.
282251881Speter               * The kind will be set during the next 'update'. */
283251881Speter              external_kind = svn_node_unknown;
284251881Speter            }
285251881Speter
286251881Speter          if (err)
287251881Speter            goto handle_error;
288251881Speter
289251881Speter          peg_revision = (item->peg_revision.kind == svn_opt_revision_number
290251881Speter                          ? item->peg_revision.value.number
291251881Speter                          : SVN_INVALID_REVNUM);
292251881Speter
293251881Speter          revision = (item->revision.kind == svn_opt_revision_number
294251881Speter                      ? item->revision.value.number
295251881Speter                      : SVN_INVALID_REVNUM);
296251881Speter
297251881Speter          err = svn_wc__upgrade_add_external_info(ctx->wc_ctx,
298251881Speter                                                  external_abspath,
299251881Speter                                                  external_kind,
300251881Speter                                                  externals_parent,
301251881Speter                                                  repos_relpath,
302251881Speter                                                  repos_root_url,
303251881Speter                                                  repos_uuid,
304251881Speter                                                  peg_revision,
305251881Speter                                                  revision,
306251881Speter                                                  iterpool2);
307251881Speterhandle_error:
308251881Speter          if (err)
309251881Speter            {
310251881Speter              svn_wc_notify_t *notify =
311251881Speter                  svn_wc_create_notify(external_abspath,
312251881Speter                                       svn_wc_notify_failed_external,
313251881Speter                                       scratch_pool);
314251881Speter              notify->err = err;
315251881Speter              ctx->notify_func2(ctx->notify_baton2,
316251881Speter                                notify, scratch_pool);
317251881Speter              svn_error_clear(err);
318251881Speter              /* Next external node, please... */
319251881Speter            }
320251881Speter        }
321251881Speter    }
322251881Speter
323251881Speter  svn_pool_destroy(iterpool);
324251881Speter  svn_pool_destroy(iterpool2);
325251881Speter
326251881Speter  return SVN_NO_ERROR;
327251881Speter}
328