upgrade.c revision 262250
1251881Speter/*
2251881Speter * upgrade.c:  routines for upgrading a working copy
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#include <apr_pools.h>
25251881Speter
26251881Speter#include "svn_types.h"
27251881Speter#include "svn_pools.h"
28251881Speter#include "svn_dirent_uri.h"
29251881Speter#include "svn_path.h"
30251881Speter#include "svn_hash.h"
31251881Speter
32251881Speter#include "wc.h"
33251881Speter#include "adm_files.h"
34251881Speter#include "conflicts.h"
35251881Speter#include "entries.h"
36251881Speter#include "wc_db.h"
37251881Speter#include "tree_conflicts.h"
38251881Speter#include "wc-queries.h"  /* for STMT_*  */
39251881Speter#include "workqueue.h"
40251881Speter
41251881Speter#include "svn_private_config.h"
42251881Speter#include "private/svn_wc_private.h"
43251881Speter#include "private/svn_sqlite.h"
44251881Speter#include "private/svn_token.h"
45251881Speter
46251881Speter/* WC-1.0 administrative area extensions */
47251881Speter#define SVN_WC__BASE_EXT      ".svn-base" /* for text and prop bases */
48251881Speter#define SVN_WC__WORK_EXT      ".svn-work" /* for working propfiles */
49251881Speter#define SVN_WC__REVERT_EXT    ".svn-revert" /* for reverting a replaced
50251881Speter                                               file */
51251881Speter
52251881Speter/* Old locations for storing "wcprops" (aka "dav cache").  */
53251881Speter#define WCPROPS_SUBDIR_FOR_FILES "wcprops"
54251881Speter#define WCPROPS_FNAME_FOR_DIR "dir-wcprops"
55251881Speter#define WCPROPS_ALL_DATA "all-wcprops"
56251881Speter
57251881Speter/* Old property locations. */
58251881Speter#define PROPS_SUBDIR "props"
59251881Speter#define PROP_BASE_SUBDIR "prop-base"
60251881Speter#define PROP_BASE_FOR_DIR "dir-prop-base"
61251881Speter#define PROP_REVERT_FOR_DIR "dir-prop-revert"
62251881Speter#define PROP_WORKING_FOR_DIR "dir-props"
63251881Speter
64251881Speter/* Old textbase location. */
65251881Speter#define TEXT_BASE_SUBDIR "text-base"
66251881Speter
67251881Speter#define TEMP_DIR "tmp"
68251881Speter
69251881Speter/* Old data files that we no longer need/use.  */
70251881Speter#define ADM_README "README.txt"
71251881Speter#define ADM_EMPTY_FILE "empty-file"
72251881Speter#define ADM_LOG "log"
73251881Speter#define ADM_LOCK "lock"
74251881Speter
75251881Speter/* New pristine location */
76251881Speter#define PRISTINE_STORAGE_RELPATH "pristine"
77251881Speter#define PRISTINE_STORAGE_EXT ".svn-base"
78251881Speter/* Number of characters in a pristine file basename, in WC format <= 28. */
79251881Speter#define PRISTINE_BASENAME_OLD_LEN 40
80251881Speter#define SDB_FILE  "wc.db"
81251881Speter
82251881Speter
83251881Speter/* Read the properties from the file at PROPFILE_ABSPATH, returning them
84251881Speter   as a hash in *PROPS. If the propfile is NOT present, then NULL will
85251881Speter   be returned in *PROPS.  */
86251881Speterstatic svn_error_t *
87251881Speterread_propfile(apr_hash_t **props,
88251881Speter              const char *propfile_abspath,
89251881Speter              apr_pool_t *result_pool,
90251881Speter              apr_pool_t *scratch_pool)
91251881Speter{
92251881Speter  svn_error_t *err;
93251881Speter  svn_stream_t *stream;
94251881Speter  apr_finfo_t finfo;
95251881Speter
96251881Speter  err = svn_io_stat(&finfo, propfile_abspath, APR_FINFO_SIZE, scratch_pool);
97251881Speter
98251881Speter  if (err
99251881Speter      && (APR_STATUS_IS_ENOENT(err->apr_err)
100251881Speter          || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
101251881Speter    {
102251881Speter      svn_error_clear(err);
103251881Speter
104251881Speter      /* The propfile was not there. Signal with a NULL.  */
105251881Speter      *props = NULL;
106251881Speter      return SVN_NO_ERROR;
107251881Speter    }
108251881Speter  else
109251881Speter    SVN_ERR(err);
110251881Speter
111251881Speter  /* A 0-bytes file signals an empty property list.
112251881Speter     (mostly used for revert-props) */
113251881Speter  if (finfo.size == 0)
114251881Speter    {
115251881Speter      *props = apr_hash_make(result_pool);
116251881Speter      return SVN_NO_ERROR;
117251881Speter    }
118251881Speter
119251881Speter  SVN_ERR(svn_stream_open_readonly(&stream, propfile_abspath,
120251881Speter                                   scratch_pool, scratch_pool));
121251881Speter
122251881Speter  /* ### does this function need to be smarter? will we see zero-length
123251881Speter     ### files? see props.c::load_props(). there may be more work here.
124251881Speter     ### need a historic analysis of 1.x property storage. what will we
125251881Speter     ### actually run into?  */
126251881Speter
127251881Speter  /* ### loggy_write_properties() and immediate_install_props() write
128251881Speter     ### zero-length files for "no props", so we should be a bit smarter
129251881Speter     ### in here.  */
130251881Speter
131251881Speter  /* ### should we be forgiving in here? I say "no". if we can't be sure,
132251881Speter     ### then we could effectively corrupt the local working copy.  */
133251881Speter
134251881Speter  *props = apr_hash_make(result_pool);
135251881Speter  SVN_ERR(svn_hash_read2(*props, stream, SVN_HASH_TERMINATOR, result_pool));
136251881Speter
137251881Speter  return svn_error_trace(svn_stream_close(stream));
138251881Speter}
139251881Speter
140251881Speter
141251881Speter/* Read one proplist (allocated from RESULT_POOL) from STREAM, and place it
142251881Speter   into ALL_WCPROPS at NAME.  */
143251881Speterstatic svn_error_t *
144251881Speterread_one_proplist(apr_hash_t *all_wcprops,
145251881Speter                  const char *name,
146251881Speter                  svn_stream_t *stream,
147251881Speter                  apr_pool_t *result_pool,
148251881Speter                  apr_pool_t *scratch_pool)
149251881Speter{
150251881Speter  apr_hash_t *proplist;
151251881Speter
152251881Speter  proplist = apr_hash_make(result_pool);
153251881Speter  SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, result_pool));
154251881Speter  svn_hash_sets(all_wcprops, name, proplist);
155251881Speter
156251881Speter  return SVN_NO_ERROR;
157251881Speter}
158251881Speter
159251881Speter
160251881Speter/* Read the wcprops from all the files in the admin area of DIR_ABSPATH,
161251881Speter   returning them in *ALL_WCPROPS. Results are allocated in RESULT_POOL,
162251881Speter   and temporary allocations are performed in SCRATCH_POOL.  */
163251881Speterstatic svn_error_t *
164251881Speterread_many_wcprops(apr_hash_t **all_wcprops,
165251881Speter                  const char *dir_abspath,
166251881Speter                  apr_pool_t *result_pool,
167251881Speter                  apr_pool_t *scratch_pool)
168251881Speter{
169251881Speter  const char *propfile_abspath;
170251881Speter  apr_hash_t *wcprops;
171251881Speter  apr_hash_t *dirents;
172251881Speter  const char *props_dir_abspath;
173251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
174251881Speter  apr_hash_index_t *hi;
175251881Speter
176251881Speter  *all_wcprops = apr_hash_make(result_pool);
177251881Speter
178251881Speter  /* First, look at dir-wcprops. */
179251881Speter  propfile_abspath = svn_wc__adm_child(dir_abspath, WCPROPS_FNAME_FOR_DIR,
180251881Speter                                       scratch_pool);
181251881Speter  SVN_ERR(read_propfile(&wcprops, propfile_abspath, result_pool, iterpool));
182251881Speter  if (wcprops != NULL)
183251881Speter    svn_hash_sets(*all_wcprops, SVN_WC_ENTRY_THIS_DIR, wcprops);
184251881Speter
185251881Speter  props_dir_abspath = svn_wc__adm_child(dir_abspath, WCPROPS_SUBDIR_FOR_FILES,
186251881Speter                                        scratch_pool);
187251881Speter
188251881Speter  /* Now walk the wcprops directory. */
189251881Speter  SVN_ERR(svn_io_get_dirents3(&dirents, props_dir_abspath, TRUE,
190251881Speter                              scratch_pool, scratch_pool));
191251881Speter
192251881Speter  for (hi = apr_hash_first(scratch_pool, dirents);
193251881Speter       hi;
194251881Speter       hi = apr_hash_next(hi))
195251881Speter    {
196251881Speter      const char *name = svn__apr_hash_index_key(hi);
197251881Speter
198251881Speter      svn_pool_clear(iterpool);
199251881Speter
200251881Speter      propfile_abspath = svn_dirent_join(props_dir_abspath, name, iterpool);
201251881Speter
202251881Speter      SVN_ERR(read_propfile(&wcprops, propfile_abspath,
203251881Speter                            result_pool, iterpool));
204251881Speter      SVN_ERR_ASSERT(wcprops != NULL);
205251881Speter      svn_hash_sets(*all_wcprops, apr_pstrdup(result_pool, name), wcprops);
206251881Speter    }
207251881Speter
208251881Speter  svn_pool_destroy(iterpool);
209251881Speter  return SVN_NO_ERROR;
210251881Speter}
211251881Speter
212251881Speter
213251881Speter/* For wcprops stored in a single file in this working copy, read that
214251881Speter   file and return it in *ALL_WCPROPS, allocated in RESULT_POOL.   Use
215251881Speter   SCRATCH_POOL for temporary allocations. */
216251881Speterstatic svn_error_t *
217251881Speterread_wcprops(apr_hash_t **all_wcprops,
218251881Speter             const char *dir_abspath,
219251881Speter             apr_pool_t *result_pool,
220251881Speter             apr_pool_t *scratch_pool)
221251881Speter{
222251881Speter  svn_stream_t *stream;
223251881Speter  svn_error_t *err;
224251881Speter
225251881Speter  *all_wcprops = apr_hash_make(result_pool);
226251881Speter
227251881Speter  err = svn_wc__open_adm_stream(&stream, dir_abspath,
228251881Speter                                WCPROPS_ALL_DATA,
229251881Speter                                scratch_pool, scratch_pool);
230251881Speter
231251881Speter  /* A non-existent file means there are no props. */
232251881Speter  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
233251881Speter    {
234251881Speter      svn_error_clear(err);
235251881Speter      return SVN_NO_ERROR;
236251881Speter    }
237251881Speter  SVN_ERR(err);
238251881Speter
239251881Speter  /* Read the proplist for THIS_DIR. */
240251881Speter  SVN_ERR(read_one_proplist(*all_wcprops, SVN_WC_ENTRY_THIS_DIR, stream,
241251881Speter                            result_pool, scratch_pool));
242251881Speter
243251881Speter  /* And now, the children. */
244251881Speter  while (1729)
245251881Speter    {
246251881Speter      svn_stringbuf_t *line;
247251881Speter      svn_boolean_t eof;
248251881Speter
249251881Speter      SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, result_pool));
250251881Speter      if (eof)
251251881Speter        {
252251881Speter          if (line->len > 0)
253251881Speter            return svn_error_createf
254251881Speter              (SVN_ERR_WC_CORRUPT, NULL,
255251881Speter               _("Missing end of line in wcprops file for '%s'"),
256251881Speter               svn_dirent_local_style(dir_abspath, scratch_pool));
257251881Speter          break;
258251881Speter        }
259251881Speter      SVN_ERR(read_one_proplist(*all_wcprops, line->data, stream,
260251881Speter                                result_pool, scratch_pool));
261251881Speter    }
262251881Speter
263251881Speter  return svn_error_trace(svn_stream_close(stream));
264251881Speter}
265251881Speter
266251881Speter/* Return in CHILDREN, the list of all 1.6 versioned subdirectories
267251881Speter   which also exist on disk as directories.
268251881Speter
269251881Speter   If DELETE_DIR is not NULL set *DELETE_DIR to TRUE if the directory
270251881Speter   should be deleted after migrating to WC-NG, otherwise to FALSE.
271251881Speter
272251881Speter   If SKIP_MISSING is TRUE, don't add missing or obstructed subdirectories
273251881Speter   to the list of children.
274251881Speter   */
275251881Speterstatic svn_error_t *
276251881Speterget_versioned_subdirs(apr_array_header_t **children,
277251881Speter                      svn_boolean_t *delete_dir,
278251881Speter                      const char *dir_abspath,
279251881Speter                      svn_boolean_t skip_missing,
280251881Speter                      apr_pool_t *result_pool,
281251881Speter                      apr_pool_t *scratch_pool)
282251881Speter{
283251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
284251881Speter  apr_hash_t *entries;
285251881Speter  apr_hash_index_t *hi;
286251881Speter  svn_wc_entry_t *this_dir = NULL;
287251881Speter
288251881Speter  *children = apr_array_make(result_pool, 10, sizeof(const char *));
289251881Speter
290251881Speter  SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath,
291251881Speter                                   scratch_pool, iterpool));
292251881Speter  for (hi = apr_hash_first(scratch_pool, entries);
293251881Speter       hi;
294251881Speter       hi = apr_hash_next(hi))
295251881Speter    {
296251881Speter      const char *name = svn__apr_hash_index_key(hi);
297251881Speter      const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi);
298251881Speter      const char *child_abspath;
299251881Speter      svn_boolean_t hidden;
300251881Speter
301251881Speter      /* skip "this dir"  */
302251881Speter      if (*name == '\0')
303251881Speter        {
304251881Speter          this_dir = svn__apr_hash_index_val(hi);
305251881Speter          continue;
306251881Speter        }
307251881Speter      else if (entry->kind != svn_node_dir)
308251881Speter        continue;
309251881Speter
310251881Speter      svn_pool_clear(iterpool);
311251881Speter
312251881Speter      /* If a directory is 'hidden' skip it as subdir */
313251881Speter      SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry));
314251881Speter      if (hidden)
315251881Speter        continue;
316251881Speter
317251881Speter      child_abspath = svn_dirent_join(dir_abspath, name, scratch_pool);
318251881Speter
319251881Speter      if (skip_missing)
320251881Speter        {
321251881Speter          svn_node_kind_t kind;
322251881Speter          SVN_ERR(svn_io_check_path(child_abspath, &kind, scratch_pool));
323251881Speter
324251881Speter          if (kind != svn_node_dir)
325251881Speter            continue;
326251881Speter        }
327251881Speter
328251881Speter      APR_ARRAY_PUSH(*children, const char *) = apr_pstrdup(result_pool,
329251881Speter                                                            child_abspath);
330251881Speter    }
331251881Speter
332251881Speter  svn_pool_destroy(iterpool);
333251881Speter
334251881Speter  if (delete_dir != NULL)
335251881Speter    {
336251881Speter      *delete_dir = (this_dir != NULL)
337251881Speter                     && (this_dir->schedule == svn_wc_schedule_delete)
338251881Speter                     && ! this_dir->keep_local;
339251881Speter    }
340251881Speter
341251881Speter  return SVN_NO_ERROR;
342251881Speter}
343251881Speter
344251881Speter
345251881Speter/* Return in CHILDREN the names of all versioned *files* in SDB that
346251881Speter   are children of PARENT_RELPATH.  These files' existence on disk is
347251881Speter   not tested.
348251881Speter
349251881Speter   This set of children is intended for property upgrades.
350251881Speter   Subdirectory's properties exist in the subdirs.
351251881Speter
352251881Speter   Note that this uses just the SDB to locate children, which means
353251881Speter   that the children must have been upgraded to wc-ng format. */
354251881Speterstatic svn_error_t *
355251881Speterget_versioned_files(const apr_array_header_t **children,
356251881Speter                    const char *parent_relpath,
357251881Speter                    svn_sqlite__db_t *sdb,
358251881Speter                    apr_int64_t wc_id,
359251881Speter                    apr_pool_t *result_pool,
360251881Speter                    apr_pool_t *scratch_pool)
361251881Speter{
362251881Speter  svn_sqlite__stmt_t *stmt;
363251881Speter  apr_array_header_t *child_names;
364251881Speter  svn_boolean_t have_row;
365251881Speter
366251881Speter  /* ### just select 'file' children. do we need 'symlink' in the future?  */
367251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ALL_FILES));
368251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
369251881Speter
370251881Speter  /* ### 10 is based on Subversion's average of 8.5 files per versioned
371251881Speter     ### directory in its repository. maybe use a different value? or
372251881Speter     ### count rows first?  */
373251881Speter  child_names = apr_array_make(result_pool, 10, sizeof(const char *));
374251881Speter
375251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
376251881Speter  while (have_row)
377251881Speter    {
378251881Speter      const char *local_relpath = svn_sqlite__column_text(stmt, 0,
379251881Speter                                                          result_pool);
380251881Speter
381251881Speter      APR_ARRAY_PUSH(child_names, const char *)
382251881Speter        = svn_relpath_basename(local_relpath, result_pool);
383251881Speter
384251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
385251881Speter    }
386251881Speter
387251881Speter  *children = child_names;
388251881Speter
389251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
390251881Speter}
391251881Speter
392251881Speter
393251881Speter/* Return the path of the old-school administrative lock file
394251881Speter   associated with LOCAL_DIR_ABSPATH, allocated from RESULT_POOL. */
395251881Speterstatic const char *
396251881Speterbuild_lockfile_path(const char *local_dir_abspath,
397251881Speter                    apr_pool_t *result_pool)
398251881Speter{
399251881Speter  return svn_dirent_join_many(result_pool,
400251881Speter                              local_dir_abspath,
401251881Speter                              svn_wc_get_adm_dir(result_pool),
402251881Speter                              ADM_LOCK,
403251881Speter                              NULL);
404251881Speter}
405251881Speter
406251881Speter
407251881Speter/* Create a physical lock file in the admin directory for ABSPATH.  */
408251881Speterstatic svn_error_t *
409251881Spetercreate_physical_lock(const char *abspath, apr_pool_t *scratch_pool)
410251881Speter{
411251881Speter  const char *lock_abspath = build_lockfile_path(abspath, scratch_pool);
412251881Speter  svn_error_t *err;
413251881Speter  apr_file_t *file;
414251881Speter
415251881Speter  err = svn_io_file_open(&file, lock_abspath,
416251881Speter                         APR_WRITE | APR_CREATE | APR_EXCL,
417251881Speter                         APR_OS_DEFAULT,
418251881Speter                         scratch_pool);
419251881Speter
420251881Speter  if (err && APR_STATUS_IS_EEXIST(err->apr_err))
421251881Speter    {
422251881Speter      /* Congratulations, we just stole a physical lock from somebody */
423251881Speter      svn_error_clear(err);
424251881Speter      return SVN_NO_ERROR;
425251881Speter    }
426251881Speter
427251881Speter  return svn_error_trace(err);
428251881Speter}
429251881Speter
430251881Speter
431251881Speter/* Wipe out all the obsolete files/dirs from the administrative area.  */
432251881Speterstatic void
433251881Speterwipe_obsolete_files(const char *wcroot_abspath, apr_pool_t *scratch_pool)
434251881Speter{
435251881Speter  /* Zap unused files.  */
436251881Speter  svn_error_clear(svn_io_remove_file2(
437251881Speter                    svn_wc__adm_child(wcroot_abspath,
438251881Speter                                      SVN_WC__ADM_FORMAT,
439251881Speter                                      scratch_pool),
440251881Speter                    TRUE, scratch_pool));
441251881Speter  svn_error_clear(svn_io_remove_file2(
442251881Speter                    svn_wc__adm_child(wcroot_abspath,
443251881Speter                                      SVN_WC__ADM_ENTRIES,
444251881Speter                                      scratch_pool),
445251881Speter                    TRUE, scratch_pool));
446251881Speter  svn_error_clear(svn_io_remove_file2(
447251881Speter                    svn_wc__adm_child(wcroot_abspath,
448251881Speter                                      ADM_EMPTY_FILE,
449251881Speter                                      scratch_pool),
450251881Speter                    TRUE, scratch_pool));
451251881Speter  svn_error_clear(svn_io_remove_file2(
452251881Speter                    svn_wc__adm_child(wcroot_abspath,
453251881Speter                                      ADM_README,
454251881Speter                                      scratch_pool),
455251881Speter                    TRUE, scratch_pool));
456251881Speter
457251881Speter  /* For formats <= SVN_WC__WCPROPS_MANY_FILES_VERSION, we toss the wcprops
458251881Speter     for the directory itself, and then all the wcprops for the files.  */
459251881Speter  svn_error_clear(svn_io_remove_file2(
460251881Speter                    svn_wc__adm_child(wcroot_abspath,
461251881Speter                                      WCPROPS_FNAME_FOR_DIR,
462251881Speter                                      scratch_pool),
463251881Speter                    TRUE, scratch_pool));
464251881Speter  svn_error_clear(svn_io_remove_dir2(
465251881Speter                    svn_wc__adm_child(wcroot_abspath,
466251881Speter                                      WCPROPS_SUBDIR_FOR_FILES,
467251881Speter                                      scratch_pool),
468251881Speter                    FALSE, NULL, NULL, scratch_pool));
469251881Speter
470251881Speter  /* And for later formats, they are aggregated into one file.  */
471251881Speter  svn_error_clear(svn_io_remove_file2(
472251881Speter                    svn_wc__adm_child(wcroot_abspath,
473251881Speter                                      WCPROPS_ALL_DATA,
474251881Speter                                      scratch_pool),
475251881Speter                    TRUE, scratch_pool));
476251881Speter
477251881Speter  /* Remove the old text-base directory and the old text-base files. */
478251881Speter  svn_error_clear(svn_io_remove_dir2(
479251881Speter                    svn_wc__adm_child(wcroot_abspath,
480251881Speter                                      TEXT_BASE_SUBDIR,
481251881Speter                                      scratch_pool),
482251881Speter                    FALSE, NULL, NULL, scratch_pool));
483251881Speter
484251881Speter  /* Remove the old properties files... whole directories at a time.  */
485251881Speter  svn_error_clear(svn_io_remove_dir2(
486251881Speter                    svn_wc__adm_child(wcroot_abspath,
487251881Speter                                      PROPS_SUBDIR,
488251881Speter                                      scratch_pool),
489251881Speter                    FALSE, NULL, NULL, scratch_pool));
490251881Speter  svn_error_clear(svn_io_remove_dir2(
491251881Speter                    svn_wc__adm_child(wcroot_abspath,
492251881Speter                                      PROP_BASE_SUBDIR,
493251881Speter                                      scratch_pool),
494251881Speter                    FALSE, NULL, NULL, scratch_pool));
495251881Speter  svn_error_clear(svn_io_remove_file2(
496251881Speter                     svn_wc__adm_child(wcroot_abspath,
497251881Speter                                       PROP_WORKING_FOR_DIR,
498251881Speter                                       scratch_pool),
499251881Speter                     TRUE, scratch_pool));
500251881Speter  svn_error_clear(svn_io_remove_file2(
501251881Speter                     svn_wc__adm_child(wcroot_abspath,
502251881Speter                                      PROP_BASE_FOR_DIR,
503251881Speter                                      scratch_pool),
504251881Speter                     TRUE, scratch_pool));
505251881Speter  svn_error_clear(svn_io_remove_file2(
506251881Speter                     svn_wc__adm_child(wcroot_abspath,
507251881Speter                                      PROP_REVERT_FOR_DIR,
508251881Speter                                      scratch_pool),
509251881Speter                     TRUE, scratch_pool));
510251881Speter
511251881Speter#if 0
512251881Speter  /* ### this checks for a write-lock, and we are not (always) taking out
513251881Speter     ### a write lock in all callers.  */
514251881Speter  SVN_ERR(svn_wc__adm_cleanup_tmp_area(db, wcroot_abspath, iterpool));
515251881Speter#endif
516251881Speter
517251881Speter  /* Remove the old-style lock file LAST.  */
518251881Speter  svn_error_clear(svn_io_remove_file2(
519251881Speter                    build_lockfile_path(wcroot_abspath, scratch_pool),
520251881Speter                    TRUE, scratch_pool));
521251881Speter}
522251881Speter
523251881Spetersvn_error_t *
524251881Spetersvn_wc__wipe_postupgrade(const char *dir_abspath,
525251881Speter                         svn_boolean_t whole_admin,
526251881Speter                         svn_cancel_func_t cancel_func,
527251881Speter                         void *cancel_baton,
528251881Speter                         apr_pool_t *scratch_pool)
529251881Speter{
530251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
531251881Speter  apr_array_header_t *subdirs;
532251881Speter  svn_error_t *err;
533251881Speter  svn_boolean_t delete_dir;
534251881Speter  int i;
535251881Speter
536251881Speter  if (cancel_func)
537251881Speter    SVN_ERR((*cancel_func)(cancel_baton));
538251881Speter
539251881Speter  err = get_versioned_subdirs(&subdirs, &delete_dir, dir_abspath, TRUE,
540251881Speter                              scratch_pool, iterpool);
541251881Speter  if (err)
542251881Speter    {
543251881Speter      if (APR_STATUS_IS_ENOENT(err->apr_err))
544251881Speter        {
545251881Speter          /* An unversioned dir is obstructing a versioned dir */
546251881Speter          svn_error_clear(err);
547251881Speter          err = NULL;
548251881Speter        }
549251881Speter      svn_pool_destroy(iterpool);
550251881Speter      return svn_error_trace(err);
551251881Speter    }
552251881Speter  for (i = 0; i < subdirs->nelts; ++i)
553251881Speter    {
554251881Speter      const char *child_abspath = APR_ARRAY_IDX(subdirs, i, const char *);
555251881Speter
556251881Speter      svn_pool_clear(iterpool);
557251881Speter      SVN_ERR(svn_wc__wipe_postupgrade(child_abspath, TRUE,
558251881Speter                                       cancel_func, cancel_baton, iterpool));
559251881Speter    }
560251881Speter
561251881Speter  /* ### Should we really be ignoring errors here? */
562251881Speter  if (whole_admin)
563251881Speter    svn_error_clear(svn_io_remove_dir2(svn_wc__adm_child(dir_abspath, "",
564251881Speter                                                         iterpool),
565251881Speter                                       TRUE, NULL, NULL, iterpool));
566251881Speter  else
567251881Speter    wipe_obsolete_files(dir_abspath, scratch_pool);
568251881Speter
569251881Speter  if (delete_dir)
570251881Speter    {
571251881Speter      /* If this was a WC-NG single database copy, this directory wouldn't
572251881Speter         be here (unless it was deleted with --keep-local)
573251881Speter
574251881Speter         If the directory is empty, we can just delete it; if not we
575251881Speter         keep it.
576251881Speter       */
577251881Speter      svn_error_clear(svn_io_dir_remove_nonrecursive(dir_abspath, iterpool));
578251881Speter    }
579251881Speter
580251881Speter  svn_pool_destroy(iterpool);
581251881Speter
582251881Speter  return SVN_NO_ERROR;
583251881Speter}
584251881Speter
585251881Speter/* Ensure that ENTRY has its REPOS and UUID fields set. These will be
586251881Speter   used to establish the REPOSITORY row in the new database, and then
587251881Speter   used within the upgraded entries as they are written into the database.
588251881Speter
589251881Speter   If one or both are not available, then it attempts to retrieve this
590251881Speter   information from REPOS_CACHE. And if that fails from REPOS_INFO_FUNC,
591251881Speter   passing REPOS_INFO_BATON.
592251881Speter   Returns a user understandable error using LOCAL_ABSPATH if the
593251881Speter   information cannot be obtained.  */
594251881Speterstatic svn_error_t *
595251881Speterensure_repos_info(svn_wc_entry_t *entry,
596251881Speter                  const char *local_abspath,
597251881Speter                  svn_wc_upgrade_get_repos_info_t repos_info_func,
598251881Speter                  void *repos_info_baton,
599251881Speter                  apr_hash_t *repos_cache,
600251881Speter                  apr_pool_t *result_pool,
601251881Speter                  apr_pool_t *scratch_pool)
602251881Speter{
603251881Speter  /* Easy exit.  */
604251881Speter  if (entry->repos != NULL && entry->uuid != NULL)
605251881Speter    return SVN_NO_ERROR;
606251881Speter
607251881Speter  if ((entry->repos == NULL || entry->uuid == NULL)
608251881Speter      && entry->url)
609251881Speter    {
610251881Speter      apr_hash_index_t *hi;
611251881Speter
612251881Speter      for (hi = apr_hash_first(scratch_pool, repos_cache);
613251881Speter           hi; hi = apr_hash_next(hi))
614251881Speter        {
615251881Speter          if (svn_uri__is_ancestor(svn__apr_hash_index_key(hi), entry->url))
616251881Speter            {
617251881Speter              if (!entry->repos)
618251881Speter                entry->repos = svn__apr_hash_index_key(hi);
619251881Speter
620251881Speter              if (!entry->uuid)
621251881Speter                entry->uuid = svn__apr_hash_index_val(hi);
622251881Speter
623251881Speter              return SVN_NO_ERROR;
624251881Speter            }
625251881Speter        }
626251881Speter    }
627251881Speter
628251881Speter  if (entry->repos == NULL && repos_info_func == NULL)
629251881Speter    return svn_error_createf(
630251881Speter        SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
631251881Speter        _("Working copy '%s' can't be upgraded because the repository root is "
632251881Speter          "not available and can't be retrieved"),
633251881Speter        svn_dirent_local_style(local_abspath, scratch_pool));
634251881Speter
635251881Speter  if (entry->uuid == NULL && repos_info_func == NULL)
636251881Speter    return svn_error_createf(
637251881Speter        SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
638251881Speter        _("Working copy '%s' can't be upgraded because the repository uuid is "
639251881Speter          "not available and can't be retrieved"),
640251881Speter        svn_dirent_local_style(local_abspath, scratch_pool));
641251881Speter
642251881Speter   if (entry->url == NULL)
643251881Speter     return svn_error_createf(
644251881Speter        SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
645251881Speter        _("Working copy '%s' can't be upgraded because it doesn't have a url"),
646251881Speter        svn_dirent_local_style(local_abspath, scratch_pool));
647251881Speter
648251881Speter   return svn_error_trace((*repos_info_func)(&entry->repos, &entry->uuid,
649251881Speter                                             repos_info_baton,
650251881Speter                                             entry->url,
651251881Speter                                             result_pool, scratch_pool));
652251881Speter}
653251881Speter
654251881Speter
655251881Speter/*
656251881Speter * Read tree conflict descriptions from @a conflict_data.  Set @a *conflicts
657251881Speter * to a hash of pointers to svn_wc_conflict_description2_t objects indexed by
658251881Speter * svn_wc_conflict_description2_t.local_abspath, all newly allocated in @a
659251881Speter * pool.  @a dir_path is the path to the working copy directory whose conflicts
660251881Speter * are being read.  The conflicts read are the tree conflicts on the immediate
661251881Speter * child nodes of @a dir_path.  Do all allocations in @a pool.
662251881Speter *
663251881Speter * Note: There were some concerns about this function:
664251881Speter *
665251881Speter * ### this is BAD. the CONFLICTS structure should not be dependent upon
666251881Speter * ### DIR_PATH. each conflict should be labeled with an entry name, not
667251881Speter * ### a whole path. (and a path which happens to vary based upon invocation
668251881Speter * ### of the user client and these APIs)
669251881Speter *
670251881Speter * those assumptions were baked into former versions of the data model, so
671251881Speter * they have to stick around here.  But they have been removed from the
672251881Speter * New Way. */
673251881Speterstatic svn_error_t *
674251881Speterread_tree_conflicts(apr_hash_t **conflicts,
675251881Speter                    const char *conflict_data,
676251881Speter                    const char *dir_path,
677251881Speter                    apr_pool_t *pool)
678251881Speter{
679251881Speter  const svn_skel_t *skel;
680251881Speter  apr_pool_t *iterpool;
681251881Speter
682251881Speter  *conflicts = apr_hash_make(pool);
683251881Speter
684251881Speter  if (conflict_data == NULL)
685251881Speter    return SVN_NO_ERROR;
686251881Speter
687251881Speter  skel = svn_skel__parse(conflict_data, strlen(conflict_data), pool);
688251881Speter  if (skel == NULL)
689251881Speter    return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
690251881Speter                            _("Error parsing tree conflict skel"));
691251881Speter
692251881Speter  iterpool = svn_pool_create(pool);
693251881Speter  for (skel = skel->children; skel != NULL; skel = skel->next)
694251881Speter    {
695251881Speter      const svn_wc_conflict_description2_t *conflict;
696251881Speter
697251881Speter      svn_pool_clear(iterpool);
698251881Speter      SVN_ERR(svn_wc__deserialize_conflict(&conflict, skel, dir_path,
699251881Speter                                           pool, iterpool));
700251881Speter      if (conflict != NULL)
701251881Speter        svn_hash_sets(*conflicts,
702251881Speter                      svn_dirent_basename(conflict->local_abspath, pool),
703251881Speter                      conflict);
704251881Speter    }
705251881Speter  svn_pool_destroy(iterpool);
706251881Speter
707251881Speter  return SVN_NO_ERROR;
708251881Speter}
709251881Speter
710251881Speter/* */
711251881Speterstatic svn_error_t *
712251881Spetermigrate_single_tree_conflict_data(svn_sqlite__db_t *sdb,
713251881Speter                                  const char *tree_conflict_data,
714251881Speter                                  apr_int64_t wc_id,
715251881Speter                                  const char *local_relpath,
716251881Speter                                  apr_pool_t *scratch_pool)
717251881Speter{
718251881Speter  apr_hash_t *conflicts;
719251881Speter  apr_hash_index_t *hi;
720251881Speter  apr_pool_t *iterpool;
721251881Speter
722251881Speter  SVN_ERR(read_tree_conflicts(&conflicts, tree_conflict_data, local_relpath,
723251881Speter                              scratch_pool));
724251881Speter
725251881Speter  iterpool = svn_pool_create(scratch_pool);
726251881Speter  for (hi = apr_hash_first(scratch_pool, conflicts);
727251881Speter       hi;
728251881Speter       hi = apr_hash_next(hi))
729251881Speter    {
730251881Speter      const svn_wc_conflict_description2_t *conflict =
731251881Speter          svn__apr_hash_index_val(hi);
732251881Speter      const char *conflict_relpath;
733251881Speter      const char *conflict_data;
734251881Speter      svn_sqlite__stmt_t *stmt;
735251881Speter      svn_boolean_t have_row;
736251881Speter      svn_skel_t *skel;
737251881Speter
738251881Speter      svn_pool_clear(iterpool);
739251881Speter
740251881Speter      conflict_relpath = svn_dirent_join(local_relpath,
741251881Speter                                         svn_dirent_basename(
742251881Speter                                           conflict->local_abspath, iterpool),
743251881Speter                                         iterpool);
744251881Speter
745251881Speter      SVN_ERR(svn_wc__serialize_conflict(&skel, conflict, iterpool, iterpool));
746251881Speter      conflict_data = svn_skel__unparse(skel, iterpool)->data;
747251881Speter
748251881Speter      /* See if we need to update or insert an ACTUAL node. */
749251881Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ACTUAL_NODE));
750251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, conflict_relpath));
751251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
752251881Speter      SVN_ERR(svn_sqlite__reset(stmt));
753251881Speter
754251881Speter      if (have_row)
755251881Speter        {
756251881Speter          /* There is an existing ACTUAL row, so just update it. */
757251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
758251881Speter                                            STMT_UPDATE_ACTUAL_CONFLICT_DATA));
759251881Speter        }
760251881Speter      else
761251881Speter        {
762251881Speter          /* We need to insert an ACTUAL row with the tree conflict data. */
763251881Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
764251881Speter                                            STMT_INSERT_ACTUAL_CONFLICT_DATA));
765251881Speter        }
766251881Speter
767251881Speter      SVN_ERR(svn_sqlite__bindf(stmt, "iss", wc_id, conflict_relpath,
768251881Speter                                conflict_data));
769251881Speter      if (!have_row)
770251881Speter        SVN_ERR(svn_sqlite__bind_text(stmt, 4, local_relpath));
771251881Speter
772251881Speter      SVN_ERR(svn_sqlite__step_done(stmt));
773251881Speter    }
774251881Speter
775251881Speter  svn_pool_destroy(iterpool);
776251881Speter
777251881Speter  return SVN_NO_ERROR;
778251881Speter}
779251881Speter
780251881Speter
781251881Speter/* */
782251881Speterstatic svn_error_t *
783251881Spetermigrate_tree_conflict_data(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
784251881Speter{
785251881Speter  svn_sqlite__stmt_t *stmt;
786251881Speter  svn_boolean_t have_row;
787251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
788251881Speter
789251881Speter  /* Iterate over each node which has a set of tree conflicts, then insert
790251881Speter     all of them into the new schema.  */
791251881Speter
792251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
793251881Speter                                    STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT));
794251881Speter
795251881Speter  /* Get all the existing tree conflict data. */
796251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
797251881Speter  while (have_row)
798251881Speter    {
799251881Speter      apr_int64_t wc_id;
800251881Speter      const char *local_relpath;
801251881Speter      const char *tree_conflict_data;
802251881Speter
803251881Speter      svn_pool_clear(iterpool);
804251881Speter
805251881Speter      wc_id = svn_sqlite__column_int64(stmt, 0);
806251881Speter      local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
807251881Speter      tree_conflict_data = svn_sqlite__column_text(stmt, 2, iterpool);
808251881Speter
809251881Speter      SVN_ERR(migrate_single_tree_conflict_data(sdb, tree_conflict_data,
810251881Speter                                                wc_id, local_relpath,
811251881Speter                                                iterpool));
812251881Speter
813251881Speter      /* We don't need to do anything but step over the previously
814251881Speter         prepared statement. */
815251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
816251881Speter    }
817251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
818251881Speter
819251881Speter  /* Erase all the old tree conflict data.  */
820251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
821251881Speter                                    STMT_UPGRADE_21_ERASE_OLD_CONFLICTS));
822251881Speter  SVN_ERR(svn_sqlite__step_done(stmt));
823251881Speter
824251881Speter  svn_pool_destroy(iterpool);
825251881Speter  return SVN_NO_ERROR;
826251881Speter}
827251881Speter
828251881Speter
829251881Speterstruct bump_baton {
830251881Speter  const char *wcroot_abspath;
831251881Speter};
832251881Speter
833251881Speter/* Migrate the properties for one node (LOCAL_ABSPATH).  */
834251881Speterstatic svn_error_t *
835251881Spetermigrate_node_props(const char *dir_abspath,
836251881Speter                   const char *new_wcroot_abspath,
837251881Speter                   const char *name,
838251881Speter                   svn_sqlite__db_t *sdb,
839251881Speter                   int original_format,
840251881Speter                   apr_int64_t wc_id,
841251881Speter                   apr_pool_t *scratch_pool)
842251881Speter{
843251881Speter  const char *base_abspath;  /* old name. nowadays: "pristine"  */
844251881Speter  const char *revert_abspath;  /* old name. nowadays: "BASE"  */
845251881Speter  const char *working_abspath;  /* old name. nowadays: "ACTUAL"  */
846251881Speter  apr_hash_t *base_props;
847251881Speter  apr_hash_t *revert_props;
848251881Speter  apr_hash_t *working_props;
849251881Speter  const char *old_wcroot_abspath
850251881Speter    = svn_dirent_get_longest_ancestor(dir_abspath, new_wcroot_abspath,
851251881Speter                                      scratch_pool);
852251881Speter  const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath,
853251881Speter                                                     dir_abspath);
854251881Speter
855251881Speter  if (*name == '\0')
856251881Speter    {
857251881Speter      base_abspath = svn_wc__adm_child(dir_abspath,
858251881Speter                                       PROP_BASE_FOR_DIR, scratch_pool);
859251881Speter      revert_abspath = svn_wc__adm_child(dir_abspath,
860251881Speter                                         PROP_REVERT_FOR_DIR, scratch_pool);
861251881Speter      working_abspath = svn_wc__adm_child(dir_abspath,
862251881Speter                                          PROP_WORKING_FOR_DIR, scratch_pool);
863251881Speter    }
864251881Speter  else
865251881Speter    {
866251881Speter      const char *basedir_abspath;
867251881Speter      const char *propsdir_abspath;
868251881Speter
869251881Speter      propsdir_abspath = svn_wc__adm_child(dir_abspath, PROPS_SUBDIR,
870251881Speter                                           scratch_pool);
871251881Speter      basedir_abspath = svn_wc__adm_child(dir_abspath, PROP_BASE_SUBDIR,
872251881Speter                                          scratch_pool);
873251881Speter
874251881Speter      base_abspath = svn_dirent_join(basedir_abspath,
875251881Speter                                     apr_pstrcat(scratch_pool,
876251881Speter                                                 name,
877251881Speter                                                 SVN_WC__BASE_EXT,
878251881Speter                                                 (char *)NULL),
879251881Speter                                     scratch_pool);
880251881Speter
881251881Speter      revert_abspath = svn_dirent_join(basedir_abspath,
882251881Speter                                       apr_pstrcat(scratch_pool,
883251881Speter                                                   name,
884251881Speter                                                   SVN_WC__REVERT_EXT,
885251881Speter                                                   (char *)NULL),
886251881Speter                                       scratch_pool);
887251881Speter
888251881Speter      working_abspath = svn_dirent_join(propsdir_abspath,
889251881Speter                                        apr_pstrcat(scratch_pool,
890251881Speter                                                    name,
891251881Speter                                                    SVN_WC__WORK_EXT,
892251881Speter                                                    (char *)NULL),
893251881Speter                                        scratch_pool);
894251881Speter    }
895251881Speter
896251881Speter  SVN_ERR(read_propfile(&base_props, base_abspath,
897251881Speter                        scratch_pool, scratch_pool));
898251881Speter  SVN_ERR(read_propfile(&revert_props, revert_abspath,
899251881Speter                        scratch_pool, scratch_pool));
900251881Speter  SVN_ERR(read_propfile(&working_props, working_abspath,
901251881Speter                        scratch_pool, scratch_pool));
902251881Speter
903251881Speter  return svn_error_trace(svn_wc__db_upgrade_apply_props(
904251881Speter                            sdb, new_wcroot_abspath,
905251881Speter                            svn_relpath_join(dir_relpath, name, scratch_pool),
906251881Speter                            base_props, revert_props, working_props,
907251881Speter                            original_format, wc_id,
908251881Speter                            scratch_pool));
909251881Speter}
910251881Speter
911251881Speter
912251881Speter/* */
913251881Speterstatic svn_error_t *
914251881Spetermigrate_props(const char *dir_abspath,
915251881Speter              const char *new_wcroot_abspath,
916251881Speter              svn_sqlite__db_t *sdb,
917251881Speter              int original_format,
918251881Speter              apr_int64_t wc_id,
919251881Speter              apr_pool_t *scratch_pool)
920251881Speter{
921251881Speter  /* General logic here: iterate over all the immediate children of the root
922251881Speter     (since we aren't yet in a centralized system), and for any properties that
923251881Speter     exist, map them as follows:
924251881Speter
925251881Speter     if (revert props exist):
926251881Speter       revert  -> BASE
927251881Speter       base    -> WORKING
928251881Speter       working -> ACTUAL
929251881Speter     else if (prop pristine is working [as defined in props.c] ):
930251881Speter       base    -> WORKING
931251881Speter       working -> ACTUAL
932251881Speter     else:
933251881Speter       base    -> BASE
934251881Speter       working -> ACTUAL
935251881Speter
936251881Speter     ### the middle "test" should simply look for a WORKING_NODE row
937251881Speter
938251881Speter     Note that it is legal for "working" props to be missing. That implies
939251881Speter     no local changes to the properties.
940251881Speter  */
941251881Speter  const apr_array_header_t *children;
942251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
943251881Speter  const char *old_wcroot_abspath
944251881Speter    = svn_dirent_get_longest_ancestor(dir_abspath, new_wcroot_abspath,
945251881Speter                                      scratch_pool);
946251881Speter  const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath,
947251881Speter                                                     dir_abspath);
948251881Speter  int i;
949251881Speter
950251881Speter  /* Migrate the props for "this dir".  */
951251881Speter  SVN_ERR(migrate_node_props(dir_abspath, new_wcroot_abspath, "", sdb,
952251881Speter                             original_format, wc_id, iterpool));
953251881Speter
954251881Speter  /* Iterate over all the files in this SDB.  */
955251881Speter  SVN_ERR(get_versioned_files(&children, dir_relpath, sdb, wc_id, scratch_pool,
956251881Speter                              iterpool));
957251881Speter  for (i = 0; i < children->nelts; i++)
958251881Speter    {
959251881Speter      const char *name = APR_ARRAY_IDX(children, i, const char *);
960251881Speter
961251881Speter      svn_pool_clear(iterpool);
962251881Speter
963251881Speter      SVN_ERR(migrate_node_props(dir_abspath, new_wcroot_abspath,
964251881Speter                                 name, sdb, original_format, wc_id, iterpool));
965251881Speter    }
966251881Speter
967251881Speter  svn_pool_destroy(iterpool);
968251881Speter
969251881Speter  return SVN_NO_ERROR;
970251881Speter}
971251881Speter
972251881Speter
973251881Speter/* If STR ends with SUFFIX and is longer than SUFFIX, return the part of
974251881Speter * STR that comes before SUFFIX; else return NULL. */
975251881Speterstatic char *
976251881Speterremove_suffix(const char *str, const char *suffix, apr_pool_t *result_pool)
977251881Speter{
978251881Speter  size_t str_len = strlen(str);
979251881Speter  size_t suffix_len = strlen(suffix);
980251881Speter
981251881Speter  if (str_len > suffix_len
982251881Speter      && strcmp(str + str_len - suffix_len, suffix) == 0)
983251881Speter    {
984251881Speter      return apr_pstrmemdup(result_pool, str, str_len - suffix_len);
985251881Speter    }
986251881Speter
987251881Speter  return NULL;
988251881Speter}
989251881Speter
990251881Speter/* Copy all the text-base files from the administrative area of WC directory
991251881Speter   DIR_ABSPATH into the pristine store of SDB which is located in directory
992251881Speter   NEW_WCROOT_ABSPATH.
993251881Speter
994251881Speter   Set *TEXT_BASES_INFO to a new hash, allocated in RESULT_POOL, that maps
995251881Speter   (const char *) name of the versioned file to (svn_wc__text_base_info_t *)
996251881Speter   information about the pristine text. */
997251881Speterstatic svn_error_t *
998251881Spetermigrate_text_bases(apr_hash_t **text_bases_info,
999251881Speter                   const char *dir_abspath,
1000251881Speter                   const char *new_wcroot_abspath,
1001251881Speter                   svn_sqlite__db_t *sdb,
1002251881Speter                   apr_pool_t *result_pool,
1003251881Speter                   apr_pool_t *scratch_pool)
1004251881Speter{
1005251881Speter  apr_hash_t *dirents;
1006251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1007251881Speter  apr_hash_index_t *hi;
1008251881Speter  const char *text_base_dir = svn_wc__adm_child(dir_abspath,
1009251881Speter                                                TEXT_BASE_SUBDIR,
1010251881Speter                                                scratch_pool);
1011251881Speter
1012251881Speter  *text_bases_info = apr_hash_make(result_pool);
1013251881Speter
1014251881Speter  /* Iterate over the text-base files */
1015251881Speter  SVN_ERR(svn_io_get_dirents3(&dirents, text_base_dir, TRUE,
1016251881Speter                              scratch_pool, scratch_pool));
1017251881Speter  for (hi = apr_hash_first(scratch_pool, dirents); hi;
1018251881Speter       hi = apr_hash_next(hi))
1019251881Speter    {
1020251881Speter      const char *text_base_basename = svn__apr_hash_index_key(hi);
1021251881Speter      svn_checksum_t *md5_checksum;
1022251881Speter      svn_checksum_t *sha1_checksum;
1023251881Speter
1024251881Speter      svn_pool_clear(iterpool);
1025251881Speter
1026251881Speter      /* Calculate its checksums and copy it to the pristine store */
1027251881Speter      {
1028251881Speter        const char *pristine_path;
1029251881Speter        const char *text_base_path;
1030251881Speter        const char *temp_path;
1031251881Speter        svn_sqlite__stmt_t *stmt;
1032251881Speter        apr_finfo_t finfo;
1033251881Speter        svn_stream_t *read_stream;
1034251881Speter        svn_stream_t *result_stream;
1035251881Speter
1036251881Speter        text_base_path = svn_dirent_join(text_base_dir, text_base_basename,
1037251881Speter                                         iterpool);
1038251881Speter
1039251881Speter        /* Create a copy and calculate a checksum in one step */
1040251881Speter        SVN_ERR(svn_stream_open_unique(&result_stream, &temp_path,
1041251881Speter                                       new_wcroot_abspath,
1042251881Speter                                       svn_io_file_del_none,
1043251881Speter                                       iterpool, iterpool));
1044251881Speter
1045251881Speter        SVN_ERR(svn_stream_open_readonly(&read_stream, text_base_path,
1046251881Speter                                           iterpool, iterpool));
1047251881Speter
1048251881Speter        read_stream = svn_stream_checksummed2(read_stream, &md5_checksum,
1049251881Speter                                              NULL, svn_checksum_md5,
1050251881Speter                                              TRUE, iterpool);
1051251881Speter
1052251881Speter        read_stream = svn_stream_checksummed2(read_stream, &sha1_checksum,
1053251881Speter                                              NULL, svn_checksum_sha1,
1054251881Speter                                              TRUE, iterpool);
1055251881Speter
1056251881Speter        /* This calculates the hash, creates a copy and closes the stream */
1057251881Speter        SVN_ERR(svn_stream_copy3(read_stream, result_stream,
1058251881Speter                                 NULL, NULL, iterpool));
1059251881Speter
1060251881Speter        SVN_ERR(svn_io_stat(&finfo, text_base_path, APR_FINFO_SIZE, iterpool));
1061251881Speter
1062251881Speter        /* Insert a row into the pristine table. */
1063251881Speter        SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1064251881Speter                                          STMT_INSERT_OR_IGNORE_PRISTINE));
1065251881Speter        SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, iterpool));
1066251881Speter        SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, iterpool));
1067251881Speter        SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
1068251881Speter        SVN_ERR(svn_sqlite__insert(NULL, stmt));
1069251881Speter
1070251881Speter        SVN_ERR(svn_wc__db_pristine_get_future_path(&pristine_path,
1071251881Speter                                                    new_wcroot_abspath,
1072251881Speter                                                    sha1_checksum,
1073251881Speter                                                    iterpool, iterpool));
1074251881Speter
1075251881Speter        /* Ensure any sharding directories exist. */
1076251881Speter        SVN_ERR(svn_wc__ensure_directory(svn_dirent_dirname(pristine_path,
1077251881Speter                                                            iterpool),
1078251881Speter                                         iterpool));
1079251881Speter
1080251881Speter        /* Now move the file into the pristine store, overwriting
1081251881Speter           existing files with the same checksum. */
1082251881Speter        SVN_ERR(svn_io_file_move(temp_path, pristine_path, iterpool));
1083251881Speter      }
1084251881Speter
1085251881Speter      /* Add the checksums for this text-base to *TEXT_BASES_INFO. */
1086251881Speter      {
1087251881Speter        const char *versioned_file_name;
1088251881Speter        svn_boolean_t is_revert_base;
1089251881Speter        svn_wc__text_base_info_t *info;
1090251881Speter        svn_wc__text_base_file_info_t *file_info;
1091251881Speter
1092251881Speter        /* Determine the versioned file name and whether this is a normal base
1093251881Speter         * or a revert base. */
1094251881Speter        versioned_file_name = remove_suffix(text_base_basename,
1095251881Speter                                            SVN_WC__REVERT_EXT, result_pool);
1096251881Speter        if (versioned_file_name)
1097251881Speter          {
1098251881Speter            is_revert_base = TRUE;
1099251881Speter          }
1100251881Speter        else
1101251881Speter          {
1102251881Speter            versioned_file_name = remove_suffix(text_base_basename,
1103251881Speter                                                SVN_WC__BASE_EXT, result_pool);
1104251881Speter            is_revert_base = FALSE;
1105251881Speter          }
1106251881Speter
1107251881Speter        if (! versioned_file_name)
1108251881Speter          {
1109251881Speter             /* Some file that doesn't end with .svn-base or .svn-revert.
1110251881Speter                No idea why that would be in our administrative area, but
1111251881Speter                we shouldn't segfault on this case.
1112251881Speter
1113251881Speter                Note that we already copied this file in the pristine store,
1114251881Speter                but the next cleanup will take care of that.
1115251881Speter              */
1116251881Speter            continue;
1117251881Speter          }
1118251881Speter
1119251881Speter        /* Create a new info struct for this versioned file, or fill in the
1120251881Speter         * existing one if this is the second text-base we've found for it. */
1121251881Speter        info = svn_hash_gets(*text_bases_info, versioned_file_name);
1122251881Speter        if (info == NULL)
1123251881Speter          info = apr_pcalloc(result_pool, sizeof (*info));
1124251881Speter        file_info = (is_revert_base ? &info->revert_base : &info->normal_base);
1125251881Speter
1126251881Speter        file_info->sha1_checksum = svn_checksum_dup(sha1_checksum, result_pool);
1127251881Speter        file_info->md5_checksum = svn_checksum_dup(md5_checksum, result_pool);
1128251881Speter        svn_hash_sets(*text_bases_info, versioned_file_name, info);
1129251881Speter      }
1130251881Speter    }
1131251881Speter
1132251881Speter  svn_pool_destroy(iterpool);
1133251881Speter
1134251881Speter  return SVN_NO_ERROR;
1135251881Speter}
1136251881Speter
1137251881Speterstatic svn_error_t *
1138251881Speterbump_to_20(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1139251881Speter{
1140251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_NODES));
1141251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_20));
1142251881Speter  return SVN_NO_ERROR;
1143251881Speter}
1144251881Speter
1145251881Speterstatic svn_error_t *
1146251881Speterbump_to_21(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1147251881Speter{
1148251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_21));
1149251881Speter  SVN_ERR(migrate_tree_conflict_data(sdb, scratch_pool));
1150251881Speter  return SVN_NO_ERROR;
1151251881Speter}
1152251881Speter
1153251881Speterstatic svn_error_t *
1154251881Speterbump_to_22(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1155251881Speter{
1156251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_22));
1157251881Speter  return SVN_NO_ERROR;
1158251881Speter}
1159251881Speter
1160251881Speterstatic svn_error_t *
1161251881Speterbump_to_23(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1162251881Speter{
1163251881Speter  const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath;
1164251881Speter  svn_sqlite__stmt_t *stmt;
1165251881Speter  svn_boolean_t have_row;
1166251881Speter
1167251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1168251881Speter                                    STMT_UPGRADE_23_HAS_WORKING_NODES));
1169251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1170251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1171251881Speter  if (have_row)
1172251881Speter    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1173251881Speter                             _("The working copy at '%s' is format 22 with "
1174251881Speter                               "WORKING nodes; use a format 22 client to "
1175251881Speter                               "diff/revert before using this client"),
1176251881Speter                             wcroot_abspath);
1177251881Speter
1178251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_23));
1179251881Speter  return SVN_NO_ERROR;
1180251881Speter}
1181251881Speter
1182251881Speterstatic svn_error_t *
1183251881Speterbump_to_24(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1184251881Speter{
1185251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_24));
1186251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_NODES_TRIGGERS));
1187251881Speter  return SVN_NO_ERROR;
1188251881Speter}
1189251881Speter
1190251881Speterstatic svn_error_t *
1191251881Speterbump_to_25(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1192251881Speter{
1193251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_25));
1194251881Speter  return SVN_NO_ERROR;
1195251881Speter}
1196251881Speter
1197251881Speterstatic svn_error_t *
1198251881Speterbump_to_26(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1199251881Speter{
1200251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_26));
1201251881Speter  return SVN_NO_ERROR;
1202251881Speter}
1203251881Speter
1204251881Speterstatic svn_error_t *
1205251881Speterbump_to_27(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1206251881Speter{
1207251881Speter  const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath;
1208251881Speter  svn_sqlite__stmt_t *stmt;
1209251881Speter  svn_boolean_t have_row;
1210251881Speter
1211251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1212251881Speter                                  STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS));
1213251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1214251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1215251881Speter  if (have_row)
1216251881Speter    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1217251881Speter                             _("The working copy at '%s' is format 26 with "
1218251881Speter                               "conflicts; use a format 26 client to resolve "
1219251881Speter                               "before using this client"),
1220251881Speter                             wcroot_abspath);
1221251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_27));
1222251881Speter  return SVN_NO_ERROR;
1223251881Speter}
1224251881Speter
1225251881Speterstatic svn_error_t *
1226251881Speterbump_to_28(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1227251881Speter{
1228251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_28));
1229251881Speter  return SVN_NO_ERROR;
1230251881Speter}
1231251881Speter
1232251881Speter/* If FINFO indicates that ABSPATH names a file, rename it to
1233251881Speter * '<ABSPATH>.svn-base'.
1234251881Speter *
1235251881Speter * Ignore any file whose name is not the expected length, in order to make
1236251881Speter * life easier for any developer who runs this code twice or has some
1237251881Speter * non-standard files in the pristine directory.
1238251881Speter *
1239251881Speter * A callback for bump_to_29(), implementing #svn_io_walk_func_t. */
1240251881Speterstatic svn_error_t *
1241251881Speterrename_pristine_file(void *baton,
1242251881Speter                     const char *abspath,
1243251881Speter                     const apr_finfo_t *finfo,
1244251881Speter                     apr_pool_t *pool)
1245251881Speter{
1246251881Speter  if (finfo->filetype == APR_REG
1247251881Speter      && (strlen(svn_dirent_basename(abspath, pool))
1248251881Speter          == PRISTINE_BASENAME_OLD_LEN))
1249251881Speter    {
1250251881Speter      const char *new_abspath
1251251881Speter        = apr_pstrcat(pool, abspath, PRISTINE_STORAGE_EXT, (char *)NULL);
1252251881Speter
1253251881Speter      SVN_ERR(svn_io_file_rename(abspath, new_abspath, pool));
1254251881Speter    }
1255251881Speter  return SVN_NO_ERROR;
1256251881Speter}
1257251881Speter
1258251881Speterstatic svn_error_t *
1259251881Speterupgrade_externals(struct bump_baton *bb,
1260251881Speter                  svn_sqlite__db_t *sdb,
1261251881Speter                  apr_pool_t *scratch_pool)
1262251881Speter{
1263251881Speter  svn_sqlite__stmt_t *stmt;
1264251881Speter  svn_sqlite__stmt_t *stmt_add;
1265251881Speter  svn_boolean_t have_row;
1266251881Speter  apr_pool_t *iterpool;
1267251881Speter
1268251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1269251881Speter                                    STMT_SELECT_EXTERNAL_PROPERTIES));
1270251881Speter
1271251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_add, sdb,
1272251881Speter                                    STMT_INSERT_EXTERNAL));
1273251881Speter
1274251881Speter  /* ### For this intermediate upgrade we just assume WC_ID = 1.
1275251881Speter     ### Before this bump we lost track of externals all the time,
1276251881Speter     ### so lets keep this easy. */
1277251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", (apr_int64_t)1, ""));
1278251881Speter
1279251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1280251881Speter
1281251881Speter  iterpool = svn_pool_create(scratch_pool);
1282251881Speter  while (have_row)
1283251881Speter    {
1284251881Speter      apr_hash_t *props;
1285251881Speter      const char *externals;
1286251881Speter
1287251881Speter      svn_pool_clear(iterpool);
1288251881Speter
1289251881Speter      SVN_ERR(svn_sqlite__column_properties(&props, stmt, 0,
1290251881Speter                                            iterpool, iterpool));
1291251881Speter
1292251881Speter      externals = svn_prop_get_value(props, SVN_PROP_EXTERNALS);
1293251881Speter
1294251881Speter      if (externals)
1295251881Speter        {
1296251881Speter          apr_array_header_t *ext;
1297251881Speter          const char *local_relpath;
1298251881Speter          const char *local_abspath;
1299251881Speter          int i;
1300251881Speter
1301251881Speter          local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
1302251881Speter          local_abspath = svn_dirent_join(bb->wcroot_abspath, local_relpath,
1303251881Speter                                          iterpool);
1304251881Speter
1305251881Speter          SVN_ERR(svn_wc_parse_externals_description3(&ext, local_abspath,
1306251881Speter                                                      externals, FALSE,
1307251881Speter                                                      iterpool));
1308251881Speter
1309251881Speter          for (i = 0; i < ext->nelts; i++)
1310251881Speter            {
1311251881Speter              const svn_wc_external_item2_t *item;
1312251881Speter              const char *item_relpath;
1313251881Speter
1314251881Speter              item = APR_ARRAY_IDX(ext, i, const svn_wc_external_item2_t *);
1315251881Speter              item_relpath = svn_relpath_join(local_relpath, item->target_dir,
1316251881Speter                                              iterpool);
1317251881Speter
1318251881Speter              /* Insert dummy externals definitions: Insert an unknown
1319251881Speter                 external, to make sure it will be cleaned up when it is not
1320251881Speter                 updated on the next update. */
1321251881Speter              SVN_ERR(svn_sqlite__bindf(stmt_add, "isssssis",
1322251881Speter                                        (apr_int64_t)1, /* wc_id */
1323251881Speter                                        item_relpath,
1324251881Speter                                        svn_relpath_dirname(item_relpath,
1325251881Speter                                                            iterpool),
1326251881Speter                                        "normal",
1327251881Speter                                        "unknown",
1328251881Speter                                        local_relpath,
1329251881Speter                                        (apr_int64_t)1, /* repos_id */
1330251881Speter                                        "" /* repos_relpath */));
1331251881Speter              SVN_ERR(svn_sqlite__insert(NULL, stmt_add));
1332251881Speter            }
1333251881Speter        }
1334251881Speter
1335251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1336251881Speter    }
1337251881Speter
1338251881Speter  svn_pool_destroy(iterpool);
1339251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
1340251881Speter}
1341251881Speter
1342251881Speterstatic svn_error_t *
1343251881Speterbump_to_29(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1344251881Speter{
1345251881Speter  struct bump_baton *bb = baton;
1346251881Speter  const char *wcroot_abspath = bb->wcroot_abspath;
1347251881Speter  const char *pristine_dir_abspath;
1348251881Speter
1349251881Speter  /* Rename all pristine files, adding a ".svn-base" suffix. */
1350251881Speter  pristine_dir_abspath = svn_dirent_join_many(scratch_pool, wcroot_abspath,
1351251881Speter                                              svn_wc_get_adm_dir(scratch_pool),
1352251881Speter                                              PRISTINE_STORAGE_RELPATH, NULL);
1353251881Speter  SVN_ERR(svn_io_dir_walk2(pristine_dir_abspath, APR_FINFO_MIN,
1354251881Speter                           rename_pristine_file, NULL, scratch_pool));
1355251881Speter
1356251881Speter  /* Externals */
1357251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_EXTERNALS));
1358251881Speter
1359251881Speter  SVN_ERR(upgrade_externals(bb, sdb, scratch_pool));
1360251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_29));
1361251881Speter  return SVN_NO_ERROR;
1362251881Speter}
1363251881Speter
1364251881Spetersvn_error_t *
1365251881Spetersvn_wc__upgrade_conflict_skel_from_raw(svn_skel_t **conflicts,
1366251881Speter                                       svn_wc__db_t *db,
1367251881Speter                                       const char *wri_abspath,
1368251881Speter                                       const char *local_relpath,
1369251881Speter                                       const char *conflict_old,
1370251881Speter                                       const char *conflict_wrk,
1371251881Speter                                       const char *conflict_new,
1372251881Speter                                       const char *prej_file,
1373251881Speter                                       const char *tree_conflict_data,
1374251881Speter                                       apr_size_t tree_conflict_len,
1375251881Speter                                       apr_pool_t *result_pool,
1376251881Speter                                       apr_pool_t *scratch_pool)
1377251881Speter{
1378251881Speter  svn_skel_t *conflict_data = NULL;
1379251881Speter  const char *wcroot_abspath;
1380251881Speter
1381251881Speter  SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
1382251881Speter                                scratch_pool, scratch_pool));
1383251881Speter
1384251881Speter  if (conflict_old || conflict_new || conflict_wrk)
1385251881Speter    {
1386251881Speter      const char *old_abspath = NULL;
1387251881Speter      const char *new_abspath = NULL;
1388251881Speter      const char *wrk_abspath = NULL;
1389251881Speter
1390251881Speter      conflict_data = svn_wc__conflict_skel_create(result_pool);
1391251881Speter
1392251881Speter      if (conflict_old)
1393251881Speter        old_abspath = svn_dirent_join(wcroot_abspath, conflict_old,
1394251881Speter                                      scratch_pool);
1395251881Speter
1396251881Speter      if (conflict_new)
1397251881Speter        new_abspath = svn_dirent_join(wcroot_abspath, conflict_new,
1398251881Speter                                      scratch_pool);
1399251881Speter
1400251881Speter      if (conflict_wrk)
1401251881Speter        wrk_abspath = svn_dirent_join(wcroot_abspath, conflict_wrk,
1402251881Speter                                      scratch_pool);
1403251881Speter
1404251881Speter      SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflict_data,
1405251881Speter                                                      db, wri_abspath,
1406251881Speter                                                      wrk_abspath,
1407251881Speter                                                      old_abspath,
1408251881Speter                                                      new_abspath,
1409251881Speter                                                      scratch_pool,
1410251881Speter                                                      scratch_pool));
1411251881Speter    }
1412251881Speter
1413251881Speter  if (prej_file)
1414251881Speter    {
1415251881Speter      const char *prej_abspath;
1416251881Speter
1417251881Speter      if (!conflict_data)
1418251881Speter        conflict_data = svn_wc__conflict_skel_create(result_pool);
1419251881Speter
1420251881Speter      prej_abspath = svn_dirent_join(wcroot_abspath, prej_file, scratch_pool);
1421251881Speter
1422251881Speter      SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflict_data,
1423251881Speter                                                      db, wri_abspath,
1424251881Speter                                                      prej_abspath,
1425251881Speter                                                      NULL, NULL, NULL,
1426251881Speter                                                apr_hash_make(scratch_pool),
1427251881Speter                                                      scratch_pool,
1428251881Speter                                                      scratch_pool));
1429251881Speter    }
1430251881Speter
1431251881Speter  if (tree_conflict_data)
1432251881Speter    {
1433251881Speter      svn_skel_t *tc_skel;
1434251881Speter      const svn_wc_conflict_description2_t *tc;
1435251881Speter      const char *local_abspath;
1436251881Speter
1437251881Speter      if (!conflict_data)
1438251881Speter        conflict_data = svn_wc__conflict_skel_create(scratch_pool);
1439251881Speter
1440251881Speter      tc_skel = svn_skel__parse(tree_conflict_data, tree_conflict_len,
1441251881Speter                                scratch_pool);
1442251881Speter
1443251881Speter      local_abspath = svn_dirent_join(wcroot_abspath, local_relpath,
1444251881Speter                                      scratch_pool);
1445251881Speter
1446251881Speter      SVN_ERR(svn_wc__deserialize_conflict(&tc, tc_skel,
1447251881Speter                                           svn_dirent_dirname(local_abspath,
1448251881Speter                                                              scratch_pool),
1449251881Speter                                           scratch_pool, scratch_pool));
1450251881Speter
1451251881Speter      SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(conflict_data,
1452251881Speter                                                      db, wri_abspath,
1453251881Speter                                                      tc->reason,
1454251881Speter                                                      tc->action,
1455251881Speter                                                      NULL,
1456251881Speter                                                      scratch_pool,
1457251881Speter                                                      scratch_pool));
1458251881Speter
1459251881Speter      switch (tc->operation)
1460251881Speter        {
1461251881Speter          case svn_wc_operation_update:
1462251881Speter          default:
1463251881Speter            SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data,
1464251881Speter                                                       tc->src_left_version,
1465251881Speter                                                       tc->src_right_version,
1466251881Speter                                                       scratch_pool,
1467251881Speter                                                       scratch_pool));
1468251881Speter            break;
1469251881Speter          case svn_wc_operation_switch:
1470251881Speter            SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict_data,
1471251881Speter                                                        tc->src_left_version,
1472251881Speter                                                        tc->src_right_version,
1473251881Speter                                                        scratch_pool,
1474251881Speter                                                        scratch_pool));
1475251881Speter            break;
1476251881Speter          case svn_wc_operation_merge:
1477251881Speter            SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_data,
1478251881Speter                                                       tc->src_left_version,
1479251881Speter                                                       tc->src_right_version,
1480251881Speter                                                       scratch_pool,
1481251881Speter                                                       scratch_pool));
1482251881Speter            break;
1483251881Speter        }
1484251881Speter    }
1485251881Speter  else if (conflict_data)
1486251881Speter    {
1487251881Speter      SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data, NULL, NULL,
1488251881Speter                                                  scratch_pool,
1489251881Speter                                                  scratch_pool));
1490251881Speter    }
1491251881Speter
1492251881Speter  *conflicts = conflict_data;
1493251881Speter  return SVN_NO_ERROR;
1494251881Speter}
1495251881Speter
1496251881Speter/* Helper function to upgrade a single conflict from bump_to_30 */
1497251881Speterstatic svn_error_t *
1498251881Speterbump_30_upgrade_one_conflict(svn_wc__db_t *wc_db,
1499251881Speter                             const char *wcroot_abspath,
1500251881Speter                             svn_sqlite__stmt_t *stmt,
1501251881Speter                             svn_sqlite__db_t *sdb,
1502251881Speter                             apr_pool_t *scratch_pool)
1503251881Speter{
1504251881Speter  svn_sqlite__stmt_t *stmt_store;
1505251881Speter  svn_stringbuf_t *skel_data;
1506251881Speter  svn_skel_t *conflict_data;
1507251881Speter  apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0);
1508251881Speter  const char *local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
1509251881Speter  const char *conflict_old = svn_sqlite__column_text(stmt, 2, NULL);
1510251881Speter  const char *conflict_wrk = svn_sqlite__column_text(stmt, 3, NULL);
1511251881Speter  const char *conflict_new = svn_sqlite__column_text(stmt, 4, NULL);
1512251881Speter  const char *prop_reject = svn_sqlite__column_text(stmt, 5, NULL);
1513251881Speter  apr_size_t tree_conflict_size;
1514251881Speter  const char *tree_conflict_data = svn_sqlite__column_blob(stmt, 6,
1515251881Speter                                           &tree_conflict_size, NULL);
1516251881Speter
1517251881Speter  SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(&conflict_data,
1518251881Speter                                                 wc_db, wcroot_abspath,
1519251881Speter                                                 local_relpath,
1520251881Speter                                                 conflict_old,
1521251881Speter                                                 conflict_wrk,
1522251881Speter                                                 conflict_new,
1523251881Speter                                                 prop_reject,
1524251881Speter                                                 tree_conflict_data,
1525251881Speter                                                 tree_conflict_size,
1526251881Speter                                                 scratch_pool, scratch_pool));
1527251881Speter
1528251881Speter  SVN_ERR_ASSERT(conflict_data != NULL);
1529251881Speter
1530251881Speter  skel_data = svn_skel__unparse(conflict_data, scratch_pool);
1531251881Speter
1532251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_store, sdb,
1533251881Speter                                    STMT_UPGRADE_30_SET_CONFLICT));
1534251881Speter  SVN_ERR(svn_sqlite__bindf(stmt_store, "isb", wc_id, local_relpath,
1535251881Speter                            skel_data->data, skel_data->len));
1536251881Speter  SVN_ERR(svn_sqlite__step_done(stmt_store));
1537251881Speter
1538251881Speter  return SVN_NO_ERROR;
1539251881Speter}
1540251881Speter
1541251881Speterstatic svn_error_t *
1542251881Speterbump_to_30(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1543251881Speter{
1544251881Speter  struct bump_baton *bb = baton;
1545251881Speter  svn_boolean_t have_row;
1546251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1547251881Speter  svn_sqlite__stmt_t *stmt;
1548251881Speter  svn_wc__db_t *db; /* Read only temp db */
1549251881Speter
1550251881Speter  SVN_ERR(svn_wc__db_open(&db, NULL, TRUE /* open_without_upgrade */, FALSE,
1551251881Speter                          scratch_pool, scratch_pool));
1552251881Speter
1553251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1554251881Speter                                    STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE));
1555251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1556251881Speter
1557251881Speter  while (have_row)
1558251881Speter    {
1559251881Speter      svn_error_t *err;
1560251881Speter      svn_pool_clear(iterpool);
1561251881Speter
1562251881Speter      err = bump_30_upgrade_one_conflict(db, bb->wcroot_abspath, stmt, sdb,
1563251881Speter                                         iterpool);
1564251881Speter
1565251881Speter      if (err)
1566251881Speter        {
1567251881Speter          return svn_error_trace(
1568251881Speter                    svn_error_compose_create(
1569251881Speter                            err,
1570251881Speter                            svn_sqlite__reset(stmt)));
1571251881Speter        }
1572251881Speter
1573251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1574251881Speter    }
1575251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1576251881Speter
1577251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_30));
1578251881Speter  SVN_ERR(svn_wc__db_close(db));
1579251881Speter  return SVN_NO_ERROR;
1580251881Speter}
1581251881Speter
1582251881Speterstatic svn_error_t *
1583251881Speterbump_to_31(void *baton,
1584251881Speter           svn_sqlite__db_t *sdb,
1585251881Speter           apr_pool_t *scratch_pool)
1586251881Speter{
1587251881Speter  svn_sqlite__stmt_t *stmt, *stmt_mark_switch_roots;
1588251881Speter  svn_boolean_t have_row;
1589251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1590251881Speter  apr_array_header_t *empty_iprops = apr_array_make(
1591251881Speter    scratch_pool, 0, sizeof(svn_prop_inherited_item_t *));
1592251881Speter  svn_boolean_t iprops_column_exists = FALSE;
1593251881Speter  svn_error_t *err;
1594251881Speter
1595251881Speter  /* Add the inherited_props column to NODES if it does not yet exist.
1596251881Speter   *
1597251881Speter   * When using a format >= 31 client to upgrade from old formats which
1598251881Speter   * did not yet have a NODES table, the inherited_props column has
1599251881Speter   * already been created as part of the NODES table. Attemping to add
1600251881Speter   * the inherited_props column will raise an error in this case, so check
1601251881Speter   * if the column exists first.
1602251881Speter   *
1603251881Speter   * Checking for the existence of a column before ALTER TABLE is not
1604251881Speter   * possible within SQLite. We need to run a separate query and evaluate
1605251881Speter   * its result in C first.
1606251881Speter   */
1607251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_PRAGMA_TABLE_INFO_NODES));
1608251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1609251881Speter  while (have_row)
1610251881Speter    {
1611251881Speter      const char *column_name = svn_sqlite__column_text(stmt, 1, NULL);
1612251881Speter
1613251881Speter      if (strcmp(column_name, "inherited_props") == 0)
1614251881Speter        {
1615251881Speter          iprops_column_exists = TRUE;
1616251881Speter          break;
1617251881Speter        }
1618251881Speter
1619251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1620251881Speter    }
1621251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1622251881Speter  if (!iprops_column_exists)
1623251881Speter    SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_31_ALTER_TABLE));
1624251881Speter
1625251881Speter  /* Run additional statements to finalize the upgrade to format 31. */
1626251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_31_FINALIZE));
1627251881Speter
1628251881Speter  /* Set inherited_props to an empty array for the roots of all
1629251881Speter     switched subtrees in the WC.  This allows subsequent updates
1630251881Speter     to recognize these roots as needing an iprops cache. */
1631251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1632251881Speter                                    STMT_UPGRADE_31_SELECT_WCROOT_NODES));
1633251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1634251881Speter
1635251881Speter  err = svn_sqlite__get_statement(&stmt_mark_switch_roots, sdb,
1636251881Speter                                  STMT_UPDATE_IPROP);
1637251881Speter  if (err)
1638251881Speter    return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1639251881Speter
1640251881Speter  while (have_row)
1641251881Speter    {
1642251881Speter      const char *switched_relpath = svn_sqlite__column_text(stmt, 1, NULL);
1643251881Speter      apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0);
1644251881Speter
1645251881Speter      err = svn_sqlite__bindf(stmt_mark_switch_roots, "is", wc_id,
1646251881Speter                              switched_relpath);
1647251881Speter      if (!err)
1648251881Speter        err = svn_sqlite__bind_iprops(stmt_mark_switch_roots, 3,
1649251881Speter                                      empty_iprops, iterpool);
1650251881Speter      if (!err)
1651251881Speter        err = svn_sqlite__step_done(stmt_mark_switch_roots);
1652251881Speter      if (!err)
1653251881Speter        err = svn_sqlite__step(&have_row, stmt);
1654251881Speter
1655251881Speter      if (err)
1656251881Speter        return svn_error_compose_create(
1657251881Speter                err,
1658251881Speter                svn_error_compose_create(
1659251881Speter                  /* Reset in either order is OK. */
1660251881Speter                  svn_sqlite__reset(stmt),
1661251881Speter                  svn_sqlite__reset(stmt_mark_switch_roots)));
1662251881Speter    }
1663251881Speter
1664251881Speter  err = svn_sqlite__reset(stmt_mark_switch_roots);
1665251881Speter  if (err)
1666251881Speter    return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1667251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1668251881Speter
1669251881Speter  svn_pool_destroy(iterpool);
1670251881Speter
1671251881Speter  return SVN_NO_ERROR;
1672251881Speter}
1673251881Speter
1674251881Speter
1675251881Speterstruct upgrade_data_t {
1676251881Speter  svn_sqlite__db_t *sdb;
1677251881Speter  const char *root_abspath;
1678251881Speter  apr_int64_t repos_id;
1679251881Speter  apr_int64_t wc_id;
1680251881Speter};
1681251881Speter
1682251881Speter/* Upgrade the working copy directory represented by DB/DIR_ABSPATH
1683251881Speter   from OLD_FORMAT to the wc-ng format (SVN_WC__WC_NG_VERSION)'.
1684251881Speter
1685251881Speter   Pass REPOS_INFO_FUNC, REPOS_INFO_BATON and REPOS_CACHE to
1686251881Speter   ensure_repos_info. Add the found repository root and UUID to
1687251881Speter   REPOS_CACHE if it doesn't have a cached entry for this
1688251881Speter   repository.
1689251881Speter
1690251881Speter   *DATA refers to the single root db.
1691251881Speter
1692251881Speter   Uses SCRATCH_POOL for all temporary allocation.  */
1693251881Speterstatic svn_error_t *
1694251881Speterupgrade_to_wcng(void **dir_baton,
1695251881Speter                void *parent_baton,
1696251881Speter                svn_wc__db_t *db,
1697251881Speter                const char *dir_abspath,
1698251881Speter                int old_format,
1699251881Speter                apr_int64_t wc_id,
1700251881Speter                svn_wc_upgrade_get_repos_info_t repos_info_func,
1701251881Speter                void *repos_info_baton,
1702251881Speter                apr_hash_t *repos_cache,
1703251881Speter                const struct upgrade_data_t *data,
1704251881Speter                apr_pool_t *result_pool,
1705251881Speter                apr_pool_t *scratch_pool)
1706251881Speter{
1707251881Speter  const char *logfile_path = svn_wc__adm_child(dir_abspath, ADM_LOG,
1708251881Speter                                               scratch_pool);
1709251881Speter  svn_node_kind_t logfile_on_disk_kind;
1710251881Speter  apr_hash_t *entries;
1711251881Speter  svn_wc_entry_t *this_dir;
1712251881Speter  const char *old_wcroot_abspath, *dir_relpath;
1713251881Speter  apr_hash_t *text_bases_info;
1714251881Speter  svn_error_t *err;
1715251881Speter
1716251881Speter  /* Don't try to mess with the WC if there are old log files left. */
1717251881Speter
1718251881Speter  /* Is the (first) log file present?  */
1719251881Speter  SVN_ERR(svn_io_check_path(logfile_path, &logfile_on_disk_kind,
1720251881Speter                            scratch_pool));
1721251881Speter  if (logfile_on_disk_kind == svn_node_file)
1722251881Speter    return svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
1723251881Speter                            _("Cannot upgrade with existing logs; run a "
1724251881Speter                              "cleanup operation on this working copy using "
1725251881Speter                              "a client version which is compatible with this "
1726251881Speter                              "working copy's format (such as the version "
1727251881Speter                              "you are upgrading from), then retry the "
1728251881Speter                              "upgrade with the current version"));
1729251881Speter
1730251881Speter  /* Lock this working copy directory, or steal an existing lock. Do this
1731251881Speter     BEFORE we read the entries. We don't want another process to modify the
1732251881Speter     entries after we've read them into memory.  */
1733251881Speter  SVN_ERR(create_physical_lock(dir_abspath, scratch_pool));
1734251881Speter
1735251881Speter  /* What's going on here?
1736251881Speter   *
1737251881Speter   * We're attempting to upgrade an older working copy to the new wc-ng format.
1738251881Speter   * The semantics and storage mechanisms between the two are vastly different,
1739251881Speter   * so it's going to be a bit painful.  Here's a plan for the operation:
1740251881Speter   *
1741251881Speter   * 1) Read the old 'entries' using the old-format reader.
1742251881Speter   *
1743251881Speter   * 2) Create the new DB if it hasn't already been created.
1744251881Speter   *
1745251881Speter   * 3) Use our compatibility code for writing entries to fill out the (new)
1746251881Speter   *    DB state.  Use the remembered checksums, since an entry has only the
1747251881Speter   *    MD5 not the SHA1 checksum, and in the case of a revert-base doesn't
1748251881Speter   *    even have that.
1749251881Speter   *
1750251881Speter   * 4) Convert wcprop to the wc-ng format
1751251881Speter   *
1752251881Speter   * 5) Migrate regular properties to the WC-NG DB.
1753251881Speter   */
1754251881Speter
1755251881Speter  /***** ENTRIES - READ *****/
1756251881Speter  SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath,
1757251881Speter                                   scratch_pool, scratch_pool));
1758251881Speter
1759251881Speter  this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
1760251881Speter  SVN_ERR(ensure_repos_info(this_dir, dir_abspath,
1761251881Speter                            repos_info_func, repos_info_baton,
1762251881Speter                            repos_cache,
1763251881Speter                            scratch_pool, scratch_pool));
1764251881Speter
1765251881Speter  /* Cache repos UUID pairs for when a subdir doesn't have this information */
1766251881Speter  if (!svn_hash_gets(repos_cache, this_dir->repos))
1767251881Speter    {
1768251881Speter      apr_pool_t *hash_pool = apr_hash_pool_get(repos_cache);
1769251881Speter
1770251881Speter      svn_hash_sets(repos_cache,
1771251881Speter                    apr_pstrdup(hash_pool, this_dir->repos),
1772251881Speter                    apr_pstrdup(hash_pool, this_dir->uuid));
1773251881Speter    }
1774251881Speter
1775251881Speter  old_wcroot_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
1776251881Speter                                                       data->root_abspath,
1777251881Speter                                                       scratch_pool);
1778251881Speter  dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath, dir_abspath);
1779251881Speter
1780251881Speter  /***** TEXT BASES *****/
1781251881Speter  SVN_ERR(migrate_text_bases(&text_bases_info, dir_abspath, data->root_abspath,
1782251881Speter                             data->sdb, scratch_pool, scratch_pool));
1783251881Speter
1784251881Speter  /***** ENTRIES - WRITE *****/
1785251881Speter  err = svn_wc__write_upgraded_entries(dir_baton, parent_baton, db, data->sdb,
1786251881Speter                                       data->repos_id, data->wc_id,
1787251881Speter                                       dir_abspath, data->root_abspath,
1788251881Speter                                       entries, text_bases_info,
1789251881Speter                                       result_pool, scratch_pool);
1790251881Speter  if (err && err->apr_err == SVN_ERR_WC_CORRUPT)
1791251881Speter    return svn_error_quick_wrap(err,
1792251881Speter                                _("This working copy is corrupt and "
1793251881Speter                                  "cannot be upgraded. Please check out "
1794251881Speter                                  "a new working copy."));
1795251881Speter  else
1796251881Speter    SVN_ERR(err);
1797251881Speter
1798251881Speter  /***** WC PROPS *****/
1799251881Speter  /* If we don't know precisely where the wcprops are, ignore them.  */
1800251881Speter  if (old_format != SVN_WC__WCPROPS_LOST)
1801251881Speter    {
1802251881Speter      apr_hash_t *all_wcprops;
1803251881Speter
1804251881Speter      if (old_format <= SVN_WC__WCPROPS_MANY_FILES_VERSION)
1805251881Speter        SVN_ERR(read_many_wcprops(&all_wcprops, dir_abspath,
1806251881Speter                                  scratch_pool, scratch_pool));
1807251881Speter      else
1808251881Speter        SVN_ERR(read_wcprops(&all_wcprops, dir_abspath,
1809251881Speter                             scratch_pool, scratch_pool));
1810251881Speter
1811251881Speter      SVN_ERR(svn_wc__db_upgrade_apply_dav_cache(data->sdb, dir_relpath,
1812251881Speter                                                 all_wcprops, scratch_pool));
1813251881Speter    }
1814251881Speter
1815251881Speter  /* Upgrade all the properties (including "this dir").
1816251881Speter
1817251881Speter     Note: this must come AFTER the entries have been migrated into the
1818251881Speter     database. The upgrade process needs the children in BASE_NODE and
1819251881Speter     WORKING_NODE, and to examine the resultant WORKING state.  */
1820251881Speter  SVN_ERR(migrate_props(dir_abspath, data->root_abspath, data->sdb, old_format,
1821251881Speter                        wc_id, scratch_pool));
1822251881Speter
1823251881Speter  return SVN_NO_ERROR;
1824251881Speter}
1825251881Speter
1826251881Speterconst char *
1827251881Spetersvn_wc__version_string_from_format(int wc_format)
1828251881Speter{
1829251881Speter  switch (wc_format)
1830251881Speter    {
1831251881Speter      case 4: return "<=1.3";
1832251881Speter      case 8: return "1.4";
1833251881Speter      case 9: return "1.5";
1834251881Speter      case 10: return "1.6";
1835251881Speter      case SVN_WC__WC_NG_VERSION: return "1.7";
1836251881Speter    }
1837251881Speter  return _("(unreleased development version)");
1838251881Speter}
1839251881Speter
1840251881Spetersvn_error_t *
1841251881Spetersvn_wc__upgrade_sdb(int *result_format,
1842251881Speter                    const char *wcroot_abspath,
1843251881Speter                    svn_sqlite__db_t *sdb,
1844251881Speter                    int start_format,
1845251881Speter                    apr_pool_t *scratch_pool)
1846251881Speter{
1847251881Speter  struct bump_baton bb;
1848251881Speter
1849251881Speter  bb.wcroot_abspath = wcroot_abspath;
1850251881Speter
1851251881Speter  if (start_format < SVN_WC__WC_NG_VERSION /* 12 */)
1852251881Speter    return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
1853251881Speter                             _("Working copy '%s' is too old (format %d, "
1854251881Speter                               "created by Subversion %s)"),
1855251881Speter                             svn_dirent_local_style(wcroot_abspath,
1856251881Speter                                                    scratch_pool),
1857251881Speter                             start_format,
1858251881Speter                             svn_wc__version_string_from_format(start_format));
1859251881Speter
1860251881Speter  /* Early WCNG formats no longer supported. */
1861251881Speter  if (start_format < 19)
1862251881Speter    return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
1863251881Speter                             _("Working copy '%s' is an old development "
1864251881Speter                               "version (format %d); to upgrade it, "
1865251881Speter                               "use a format 18 client, then "
1866251881Speter                               "use 'tools/dev/wc-ng/bump-to-19.py', then "
1867251881Speter                               "use the current client"),
1868251881Speter                             svn_dirent_local_style(wcroot_abspath,
1869251881Speter                                                    scratch_pool),
1870251881Speter                             start_format);
1871251881Speter
1872251881Speter  /* ### need lock-out. only one upgrade at a time. note that other code
1873251881Speter     ### cannot use this un-upgraded database until we finish the upgrade.  */
1874251881Speter
1875251881Speter  /* Note: none of these have "break" statements; the fall-through is
1876251881Speter     intentional. */
1877251881Speter  switch (start_format)
1878251881Speter    {
1879251881Speter      case 19:
1880251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_20, &bb,
1881251881Speter                                             scratch_pool));
1882251881Speter        *result_format = 20;
1883251881Speter        /* FALLTHROUGH  */
1884251881Speter
1885251881Speter      case 20:
1886251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_21, &bb,
1887251881Speter                                             scratch_pool));
1888251881Speter        *result_format = 21;
1889251881Speter        /* FALLTHROUGH  */
1890251881Speter
1891251881Speter      case 21:
1892251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_22, &bb,
1893251881Speter                                             scratch_pool));
1894251881Speter        *result_format = 22;
1895251881Speter        /* FALLTHROUGH  */
1896251881Speter
1897251881Speter      case 22:
1898251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_23, &bb,
1899251881Speter                                             scratch_pool));
1900251881Speter        *result_format = 23;
1901251881Speter        /* FALLTHROUGH  */
1902251881Speter
1903251881Speter      case 23:
1904251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_24, &bb,
1905251881Speter                                             scratch_pool));
1906251881Speter        *result_format = 24;
1907251881Speter        /* FALLTHROUGH  */
1908251881Speter
1909251881Speter      case 24:
1910251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_25, &bb,
1911251881Speter                                             scratch_pool));
1912251881Speter        *result_format = 25;
1913251881Speter        /* FALLTHROUGH  */
1914251881Speter
1915251881Speter      case 25:
1916251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_26, &bb,
1917251881Speter                                             scratch_pool));
1918251881Speter        *result_format = 26;
1919251881Speter        /* FALLTHROUGH  */
1920251881Speter
1921251881Speter      case 26:
1922251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_27, &bb,
1923251881Speter                                             scratch_pool));
1924251881Speter        *result_format = 27;
1925251881Speter        /* FALLTHROUGH  */
1926251881Speter
1927251881Speter      case 27:
1928251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_28, &bb,
1929251881Speter                                             scratch_pool));
1930251881Speter        *result_format = 28;
1931251881Speter        /* FALLTHROUGH  */
1932251881Speter
1933251881Speter      case 28:
1934251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_29, &bb,
1935251881Speter                                             scratch_pool));
1936251881Speter        *result_format = 29;
1937251881Speter        /* FALLTHROUGH  */
1938251881Speter
1939251881Speter      case 29:
1940251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_30, &bb,
1941251881Speter                                             scratch_pool));
1942251881Speter        *result_format = 30;
1943251881Speter
1944251881Speter      case 30:
1945251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_31, &bb,
1946251881Speter                                             scratch_pool));
1947251881Speter        *result_format = 31;
1948251881Speter        /* FALLTHROUGH  */
1949251881Speter      /* ### future bumps go here.  */
1950251881Speter#if 0
1951251881Speter      case XXX-1:
1952251881Speter        /* Revamp the recording of tree conflicts.  */
1953251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_XXX, &bb,
1954251881Speter                                             scratch_pool));
1955251881Speter        *result_format = XXX;
1956251881Speter        /* FALLTHROUGH  */
1957251881Speter#endif
1958251881Speter      case SVN_WC__VERSION:
1959251881Speter        /* already upgraded */
1960251881Speter        *result_format = SVN_WC__VERSION;
1961262250Speter
1962262250Speter        SVN_SQLITE__WITH_LOCK(
1963262250Speter            svn_wc__db_install_schema_statistics(sdb, scratch_pool),
1964262250Speter            sdb);
1965251881Speter    }
1966251881Speter
1967251881Speter#ifdef SVN_DEBUG
1968251881Speter  if (*result_format != start_format)
1969251881Speter    {
1970251881Speter      int schema_version;
1971251881Speter      SVN_ERR(svn_sqlite__read_schema_version(&schema_version, sdb, scratch_pool));
1972251881Speter
1973251881Speter      /* If this assertion fails the schema isn't updated correctly */
1974251881Speter      SVN_ERR_ASSERT(schema_version == *result_format);
1975251881Speter    }
1976251881Speter#endif
1977251881Speter
1978251881Speter  /* Zap anything that might be remaining or escaped our notice.  */
1979251881Speter  wipe_obsolete_files(wcroot_abspath, scratch_pool);
1980251881Speter
1981251881Speter  return SVN_NO_ERROR;
1982251881Speter}
1983251881Speter
1984251881Speter
1985251881Speter/* */
1986251881Speterstatic svn_error_t *
1987251881Speterupgrade_working_copy(void *parent_baton,
1988251881Speter                     svn_wc__db_t *db,
1989251881Speter                     const char *dir_abspath,
1990251881Speter                     svn_wc_upgrade_get_repos_info_t repos_info_func,
1991251881Speter                     void *repos_info_baton,
1992251881Speter                     apr_hash_t *repos_cache,
1993251881Speter                     const struct upgrade_data_t *data,
1994251881Speter                     svn_cancel_func_t cancel_func,
1995251881Speter                     void *cancel_baton,
1996251881Speter                     svn_wc_notify_func2_t notify_func,
1997251881Speter                     void *notify_baton,
1998251881Speter                     apr_pool_t *result_pool,
1999251881Speter                     apr_pool_t *scratch_pool)
2000251881Speter{
2001251881Speter  void *dir_baton;
2002251881Speter  int old_format;
2003251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2004251881Speter  apr_array_header_t *subdirs;
2005251881Speter  svn_error_t *err;
2006251881Speter  int i;
2007251881Speter
2008251881Speter  if (cancel_func)
2009251881Speter    SVN_ERR(cancel_func(cancel_baton));
2010251881Speter
2011251881Speter  SVN_ERR(svn_wc__db_temp_get_format(&old_format, db, dir_abspath,
2012251881Speter                                     iterpool));
2013251881Speter
2014251881Speter  if (old_format >= SVN_WC__WC_NG_VERSION)
2015251881Speter    {
2016251881Speter      if (notify_func)
2017251881Speter        notify_func(notify_baton,
2018251881Speter                    svn_wc_create_notify(dir_abspath, svn_wc_notify_skip,
2019251881Speter                                         iterpool),
2020251881Speter                iterpool);
2021251881Speter      svn_pool_destroy(iterpool);
2022251881Speter      return SVN_NO_ERROR;
2023251881Speter    }
2024251881Speter
2025251881Speter  err = get_versioned_subdirs(&subdirs, NULL, dir_abspath, FALSE,
2026251881Speter                              scratch_pool, iterpool);
2027251881Speter  if (err)
2028251881Speter    {
2029251881Speter      if (APR_STATUS_IS_ENOENT(err->apr_err)
2030251881Speter          || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))
2031251881Speter        {
2032251881Speter          /* An unversioned dir is obstructing a versioned dir */
2033251881Speter          svn_error_clear(err);
2034251881Speter          err = NULL;
2035251881Speter          if (notify_func)
2036251881Speter            notify_func(notify_baton,
2037251881Speter                        svn_wc_create_notify(dir_abspath, svn_wc_notify_skip,
2038251881Speter                                             iterpool),
2039251881Speter                        iterpool);
2040251881Speter        }
2041251881Speter      svn_pool_destroy(iterpool);
2042251881Speter      return err;
2043251881Speter    }
2044251881Speter
2045251881Speter
2046251881Speter  SVN_ERR(upgrade_to_wcng(&dir_baton, parent_baton, db, dir_abspath,
2047251881Speter                          old_format, data->wc_id,
2048251881Speter                          repos_info_func, repos_info_baton,
2049251881Speter                          repos_cache, data, scratch_pool, iterpool));
2050251881Speter
2051251881Speter  if (notify_func)
2052251881Speter    notify_func(notify_baton,
2053251881Speter                svn_wc_create_notify(dir_abspath, svn_wc_notify_upgraded_path,
2054251881Speter                                     iterpool),
2055251881Speter                iterpool);
2056251881Speter
2057251881Speter  for (i = 0; i < subdirs->nelts; ++i)
2058251881Speter    {
2059251881Speter      const char *child_abspath = APR_ARRAY_IDX(subdirs, i, const char *);
2060251881Speter
2061251881Speter      svn_pool_clear(iterpool);
2062251881Speter
2063251881Speter      SVN_ERR(upgrade_working_copy(dir_baton, db, child_abspath,
2064251881Speter                                   repos_info_func, repos_info_baton,
2065251881Speter                                   repos_cache, data,
2066251881Speter                                   cancel_func, cancel_baton,
2067251881Speter                                   notify_func, notify_baton,
2068251881Speter                                   iterpool, iterpool));
2069251881Speter    }
2070251881Speter
2071251881Speter  svn_pool_destroy(iterpool);
2072251881Speter
2073251881Speter  return SVN_NO_ERROR;
2074251881Speter}
2075251881Speter
2076251881Speter
2077251881Speter/* Return a verbose error if LOCAL_ABSPATH is a not a pre-1.7 working
2078251881Speter   copy root */
2079251881Speterstatic svn_error_t *
2080251881Speteris_old_wcroot(const char *local_abspath,
2081251881Speter              apr_pool_t *scratch_pool)
2082251881Speter{
2083251881Speter  apr_hash_t *entries;
2084251881Speter  const char *parent_abspath, *name;
2085251881Speter  svn_wc_entry_t *entry;
2086251881Speter  svn_error_t *err = svn_wc__read_entries_old(&entries, local_abspath,
2087251881Speter                                              scratch_pool, scratch_pool);
2088251881Speter  if (err)
2089251881Speter    {
2090251881Speter      return svn_error_createf(
2091251881Speter        SVN_ERR_WC_INVALID_OP_ON_CWD, err,
2092251881Speter        _("Can't upgrade '%s' as it is not a working copy"),
2093251881Speter        svn_dirent_local_style(local_abspath, scratch_pool));
2094251881Speter    }
2095251881Speter  else if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
2096251881Speter    return SVN_NO_ERROR;
2097251881Speter
2098251881Speter  svn_dirent_split(&parent_abspath, &name, local_abspath, scratch_pool);
2099251881Speter
2100251881Speter  err = svn_wc__read_entries_old(&entries, parent_abspath,
2101251881Speter                                 scratch_pool, scratch_pool);
2102251881Speter  if (err)
2103251881Speter    {
2104251881Speter      svn_error_clear(err);
2105251881Speter      return SVN_NO_ERROR;
2106251881Speter    }
2107251881Speter
2108251881Speter  entry = svn_hash_gets(entries, name);
2109251881Speter  if (!entry
2110251881Speter      || entry->absent
2111251881Speter      || (entry->deleted && entry->schedule != svn_wc_schedule_add)
2112251881Speter      || entry->depth == svn_depth_exclude)
2113251881Speter    {
2114251881Speter      return SVN_NO_ERROR;
2115251881Speter    }
2116251881Speter
2117251881Speter  while (!svn_dirent_is_root(parent_abspath, strlen(parent_abspath)))
2118251881Speter    {
2119251881Speter      svn_dirent_split(&parent_abspath, &name, parent_abspath, scratch_pool);
2120251881Speter      err = svn_wc__read_entries_old(&entries, parent_abspath,
2121251881Speter                                     scratch_pool, scratch_pool);
2122251881Speter      if (err)
2123251881Speter        {
2124251881Speter          svn_error_clear(err);
2125251881Speter          parent_abspath = svn_dirent_join(parent_abspath, name, scratch_pool);
2126251881Speter          break;
2127251881Speter        }
2128251881Speter      entry = svn_hash_gets(entries, name);
2129251881Speter      if (!entry
2130251881Speter          || entry->absent
2131251881Speter          || (entry->deleted && entry->schedule != svn_wc_schedule_add)
2132251881Speter          || entry->depth == svn_depth_exclude)
2133251881Speter        {
2134251881Speter          parent_abspath = svn_dirent_join(parent_abspath, name, scratch_pool);
2135251881Speter          break;
2136251881Speter        }
2137251881Speter    }
2138251881Speter
2139251881Speter  return svn_error_createf(
2140251881Speter    SVN_ERR_WC_INVALID_OP_ON_CWD, NULL,
2141251881Speter    _("Can't upgrade '%s' as it is not a working copy root,"
2142251881Speter      " the root is '%s'"),
2143251881Speter    svn_dirent_local_style(local_abspath, scratch_pool),
2144251881Speter    svn_dirent_local_style(parent_abspath, scratch_pool));
2145251881Speter}
2146251881Speter
2147251881Speter/* Data for upgrade_working_copy_txn(). */
2148251881Spetertypedef struct upgrade_working_copy_baton_t
2149251881Speter{
2150251881Speter  svn_wc__db_t *db;
2151251881Speter  const char *dir_abspath;
2152251881Speter  svn_wc_upgrade_get_repos_info_t repos_info_func;
2153251881Speter  void *repos_info_baton;
2154251881Speter  apr_hash_t *repos_cache;
2155251881Speter  const struct upgrade_data_t *data;
2156251881Speter  svn_cancel_func_t cancel_func;
2157251881Speter  void *cancel_baton;
2158251881Speter  svn_wc_notify_func2_t notify_func;
2159251881Speter  void *notify_baton;
2160251881Speter  apr_pool_t *result_pool;
2161251881Speter} upgrade_working_copy_baton_t;
2162251881Speter
2163251881Speter
2164251881Speter/* Helper for svn_wc_upgrade. Implements svn_sqlite__transaction_callback_t */
2165251881Speterstatic svn_error_t *
2166251881Speterupgrade_working_copy_txn(void *baton,
2167251881Speter                         svn_sqlite__db_t *sdb,
2168251881Speter                         apr_pool_t *scratch_pool)
2169251881Speter{
2170251881Speter  upgrade_working_copy_baton_t *b = baton;
2171251881Speter
2172251881Speter  /* Upgrade the pre-wcng into a wcng in a temporary location. */
2173251881Speter  return(upgrade_working_copy(NULL, b->db, b->dir_abspath,
2174251881Speter                              b->repos_info_func, b->repos_info_baton,
2175251881Speter                              b->repos_cache, b->data,
2176251881Speter                              b->cancel_func, b->cancel_baton,
2177251881Speter                              b->notify_func, b->notify_baton,
2178251881Speter                              b->result_pool, scratch_pool));
2179251881Speter}
2180251881Speter
2181251881Spetersvn_error_t *
2182251881Spetersvn_wc_upgrade(svn_wc_context_t *wc_ctx,
2183251881Speter               const char *local_abspath,
2184251881Speter               svn_wc_upgrade_get_repos_info_t repos_info_func,
2185251881Speter               void *repos_info_baton,
2186251881Speter               svn_cancel_func_t cancel_func,
2187251881Speter               void *cancel_baton,
2188251881Speter               svn_wc_notify_func2_t notify_func,
2189251881Speter               void *notify_baton,
2190251881Speter               apr_pool_t *scratch_pool)
2191251881Speter{
2192251881Speter  svn_wc__db_t *db;
2193251881Speter  struct upgrade_data_t data = { NULL };
2194251881Speter  svn_skel_t *work_item, *work_items = NULL;
2195251881Speter  const char *pristine_from, *pristine_to, *db_from, *db_to;
2196251881Speter  apr_hash_t *repos_cache = apr_hash_make(scratch_pool);
2197251881Speter  svn_wc_entry_t *this_dir;
2198251881Speter  apr_hash_t *entries;
2199251881Speter  const char *root_adm_abspath;
2200251881Speter  upgrade_working_copy_baton_t cb_baton;
2201251881Speter  svn_error_t *err;
2202251881Speter  int result_format;
2203253734Speter  svn_boolean_t bumped_format;
2204251881Speter
2205251881Speter  /* Try upgrading a wc-ng-style working copy. */
2206251881Speter  SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, TRUE, FALSE,
2207251881Speter                          scratch_pool, scratch_pool));
2208251881Speter
2209251881Speter
2210253734Speter  err = svn_wc__db_bump_format(&result_format, &bumped_format,
2211253734Speter                               db, local_abspath,
2212251881Speter                               scratch_pool);
2213251881Speter  if (err)
2214251881Speter    {
2215251881Speter      if (err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
2216251881Speter        {
2217251881Speter          return svn_error_trace(
2218251881Speter                    svn_error_compose_create(
2219251881Speter                            err,
2220251881Speter                            svn_wc__db_close(db)));
2221251881Speter        }
2222251881Speter
2223251881Speter      svn_error_clear(err);
2224251881Speter      /* Pre 1.7: Fall through */
2225251881Speter    }
2226251881Speter  else
2227251881Speter    {
2228251881Speter      /* Auto-upgrade worked! */
2229251881Speter      SVN_ERR(svn_wc__db_close(db));
2230251881Speter
2231251881Speter      SVN_ERR_ASSERT(result_format == SVN_WC__VERSION);
2232251881Speter
2233253734Speter      if (bumped_format && notify_func)
2234253734Speter        {
2235253734Speter          svn_wc_notify_t *notify;
2236253734Speter
2237253734Speter          notify = svn_wc_create_notify(local_abspath,
2238253734Speter                                        svn_wc_notify_upgraded_path,
2239253734Speter                                        scratch_pool);
2240253734Speter
2241253734Speter          notify_func(notify_baton, notify, scratch_pool);
2242253734Speter        }
2243253734Speter
2244251881Speter      return SVN_NO_ERROR;
2245251881Speter    }
2246251881Speter
2247251881Speter  SVN_ERR(is_old_wcroot(local_abspath, scratch_pool));
2248251881Speter
2249251881Speter  /* Given a pre-wcng root some/wc we create a temporary wcng in
2250251881Speter     some/wc/.svn/tmp/wcng/wc.db and copy the metadata from one to the
2251251881Speter     other, then the temporary wc.db file gets moved into the original
2252251881Speter     root.  Until the wc.db file is moved the original working copy
2253251881Speter     remains a pre-wcng and 'cleanup' with an old client will remove
2254251881Speter     the partial upgrade.  Moving the wc.db file creates a wcng, and
2255251881Speter     'cleanup' with a new client will complete any outstanding
2256251881Speter     upgrade. */
2257251881Speter
2258251881Speter  SVN_ERR(svn_wc__read_entries_old(&entries, local_abspath,
2259251881Speter                                   scratch_pool, scratch_pool));
2260251881Speter
2261251881Speter  this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2262251881Speter  SVN_ERR(ensure_repos_info(this_dir, local_abspath, repos_info_func,
2263251881Speter                            repos_info_baton, repos_cache,
2264251881Speter                            scratch_pool, scratch_pool));
2265251881Speter
2266251881Speter  /* Cache repos UUID pairs for when a subdir doesn't have this information */
2267251881Speter  if (!svn_hash_gets(repos_cache, this_dir->repos))
2268251881Speter    svn_hash_sets(repos_cache,
2269251881Speter                  apr_pstrdup(scratch_pool, this_dir->repos),
2270251881Speter                  apr_pstrdup(scratch_pool, this_dir->uuid));
2271251881Speter
2272251881Speter  /* Create the new DB in the temporary root wc/.svn/tmp/wcng/.svn */
2273251881Speter  data.root_abspath = svn_dirent_join(svn_wc__adm_child(local_abspath, "tmp",
2274251881Speter                                                        scratch_pool),
2275251881Speter                                       "wcng", scratch_pool);
2276251881Speter  root_adm_abspath = svn_wc__adm_child(data.root_abspath, "",
2277251881Speter                                       scratch_pool);
2278251881Speter  SVN_ERR(svn_io_remove_dir2(root_adm_abspath, TRUE, NULL, NULL,
2279251881Speter                             scratch_pool));
2280251881Speter  SVN_ERR(svn_wc__ensure_directory(root_adm_abspath, scratch_pool));
2281251881Speter
2282251881Speter  /* Create an empty sqlite database for this directory and store it in DB. */
2283251881Speter  SVN_ERR(svn_wc__db_upgrade_begin(&data.sdb,
2284251881Speter                                   &data.repos_id, &data.wc_id,
2285251881Speter                                   db, data.root_abspath,
2286251881Speter                                   this_dir->repos, this_dir->uuid,
2287251881Speter                                   scratch_pool));
2288251881Speter
2289251881Speter  /* Migrate the entries over to the new database.
2290251881Speter   ### We need to think about atomicity here.
2291251881Speter
2292251881Speter   entries_write_new() writes in current format rather than
2293251881Speter   f12. Thus, this function bumps a working copy all the way to
2294251881Speter   current.  */
2295251881Speter  SVN_ERR(svn_wc__db_wclock_obtain(db, data.root_abspath, 0, FALSE,
2296251881Speter                                   scratch_pool));
2297251881Speter
2298251881Speter  cb_baton.db = db;
2299251881Speter  cb_baton.dir_abspath = local_abspath;
2300251881Speter  cb_baton.repos_info_func = repos_info_func;
2301251881Speter  cb_baton.repos_info_baton = repos_info_baton;
2302251881Speter  cb_baton.repos_cache = repos_cache;
2303251881Speter  cb_baton.data = &data;
2304251881Speter  cb_baton.cancel_func = cancel_func;
2305251881Speter  cb_baton.cancel_baton = cancel_baton;
2306251881Speter  cb_baton.notify_func = notify_func;
2307251881Speter  cb_baton.notify_baton = notify_baton;
2308251881Speter  cb_baton.result_pool = scratch_pool;
2309251881Speter
2310251881Speter  SVN_ERR(svn_sqlite__with_lock(data.sdb,
2311251881Speter                                upgrade_working_copy_txn,
2312251881Speter                                &cb_baton,
2313251881Speter                                scratch_pool));
2314251881Speter
2315251881Speter  /* A workqueue item to move the pristine dir into place */
2316251881Speter  pristine_from = svn_wc__adm_child(data.root_abspath, PRISTINE_STORAGE_RELPATH,
2317251881Speter                                    scratch_pool);
2318251881Speter  pristine_to = svn_wc__adm_child(local_abspath, PRISTINE_STORAGE_RELPATH,
2319251881Speter                                  scratch_pool);
2320251881Speter  SVN_ERR(svn_wc__ensure_directory(pristine_from, scratch_pool));
2321251881Speter  SVN_ERR(svn_wc__wq_build_file_move(&work_item, db, local_abspath,
2322251881Speter                                     pristine_from, pristine_to,
2323251881Speter                                     scratch_pool, scratch_pool));
2324251881Speter  work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2325251881Speter
2326251881Speter  /* A workqueue item to remove pre-wcng metadata */
2327251881Speter  SVN_ERR(svn_wc__wq_build_postupgrade(&work_item, scratch_pool));
2328251881Speter  work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2329251881Speter  SVN_ERR(svn_wc__db_wq_add(db, data.root_abspath, work_items, scratch_pool));
2330251881Speter
2331251881Speter  SVN_ERR(svn_wc__db_wclock_release(db, data.root_abspath, scratch_pool));
2332251881Speter  SVN_ERR(svn_wc__db_close(db));
2333251881Speter
2334251881Speter  /* Renaming the db file is what makes the pre-wcng into a wcng */
2335251881Speter  db_from = svn_wc__adm_child(data.root_abspath, SDB_FILE, scratch_pool);
2336251881Speter  db_to = svn_wc__adm_child(local_abspath, SDB_FILE, scratch_pool);
2337251881Speter  SVN_ERR(svn_io_file_rename(db_from, db_to, scratch_pool));
2338251881Speter
2339251881Speter  /* Now we have a working wcng, tidy up the droppings */
2340251881Speter  SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, FALSE, FALSE,
2341251881Speter                          scratch_pool, scratch_pool));
2342251881Speter  SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2343251881Speter                         scratch_pool));
2344251881Speter  SVN_ERR(svn_wc__db_close(db));
2345251881Speter
2346251881Speter  /* Should we have the workqueue remove this empty dir? */
2347251881Speter  SVN_ERR(svn_io_remove_dir2(data.root_abspath, FALSE, NULL, NULL,
2348251881Speter                             scratch_pool));
2349251881Speter
2350251881Speter  return SVN_NO_ERROR;
2351251881Speter}
2352251881Speter
2353251881Spetersvn_error_t *
2354251881Spetersvn_wc__upgrade_add_external_info(svn_wc_context_t *wc_ctx,
2355251881Speter                                  const char *local_abspath,
2356251881Speter                                  svn_node_kind_t kind,
2357251881Speter                                  const char *def_local_abspath,
2358251881Speter                                  const char *repos_relpath,
2359251881Speter                                  const char *repos_root_url,
2360251881Speter                                  const char *repos_uuid,
2361251881Speter                                  svn_revnum_t def_peg_revision,
2362251881Speter                                  svn_revnum_t def_revision,
2363251881Speter                                  apr_pool_t *scratch_pool)
2364251881Speter{
2365251881Speter  svn_node_kind_t db_kind;
2366251881Speter  switch (kind)
2367251881Speter    {
2368251881Speter      case svn_node_dir:
2369251881Speter        db_kind = svn_node_dir;
2370251881Speter        break;
2371251881Speter
2372251881Speter      case svn_node_file:
2373251881Speter        db_kind = svn_node_file;
2374251881Speter        break;
2375251881Speter
2376251881Speter      case svn_node_unknown:
2377251881Speter        db_kind = svn_node_unknown;
2378251881Speter        break;
2379251881Speter
2380251881Speter      default:
2381251881Speter        SVN_ERR_MALFUNCTION();
2382251881Speter    }
2383251881Speter
2384251881Speter  SVN_ERR(svn_wc__db_upgrade_insert_external(wc_ctx->db, local_abspath,
2385251881Speter                                             db_kind,
2386251881Speter                                             svn_dirent_dirname(local_abspath,
2387251881Speter                                                                scratch_pool),
2388251881Speter                                             def_local_abspath, repos_relpath,
2389251881Speter                                             repos_root_url, repos_uuid,
2390251881Speter                                             def_peg_revision, def_revision,
2391251881Speter                                             scratch_pool));
2392251881Speter  return SVN_NO_ERROR;
2393251881Speter}
2394