upgrade.c revision 299742
1/*
2 * upgrade.c:  routines for upgrading a working copy
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24#include <apr_pools.h>
25
26#include "svn_types.h"
27#include "svn_pools.h"
28#include "svn_dirent_uri.h"
29#include "svn_path.h"
30#include "svn_hash.h"
31
32#include "wc.h"
33#include "adm_files.h"
34#include "conflicts.h"
35#include "entries.h"
36#include "wc_db.h"
37#include "tree_conflicts.h"
38#include "wc-queries.h"  /* for STMT_*  */
39#include "workqueue.h"
40#include "token-map.h"
41
42#include "svn_private_config.h"
43#include "private/svn_wc_private.h"
44#include "private/svn_sqlite.h"
45#include "private/svn_token.h"
46
47/* WC-1.0 administrative area extensions */
48#define SVN_WC__BASE_EXT      ".svn-base" /* for text and prop bases */
49#define SVN_WC__WORK_EXT      ".svn-work" /* for working propfiles */
50#define SVN_WC__REVERT_EXT    ".svn-revert" /* for reverting a replaced
51                                               file */
52
53/* Old locations for storing "wcprops" (aka "dav cache").  */
54#define WCPROPS_SUBDIR_FOR_FILES "wcprops"
55#define WCPROPS_FNAME_FOR_DIR "dir-wcprops"
56#define WCPROPS_ALL_DATA "all-wcprops"
57
58/* Old property locations. */
59#define PROPS_SUBDIR "props"
60#define PROP_BASE_SUBDIR "prop-base"
61#define PROP_BASE_FOR_DIR "dir-prop-base"
62#define PROP_REVERT_FOR_DIR "dir-prop-revert"
63#define PROP_WORKING_FOR_DIR "dir-props"
64
65/* Old textbase location. */
66#define TEXT_BASE_SUBDIR "text-base"
67
68#define TEMP_DIR "tmp"
69
70/* Old data files that we no longer need/use.  */
71#define ADM_README "README.txt"
72#define ADM_EMPTY_FILE "empty-file"
73#define ADM_LOG "log"
74#define ADM_LOCK "lock"
75
76/* New pristine location */
77#define PRISTINE_STORAGE_RELPATH "pristine"
78#define PRISTINE_STORAGE_EXT ".svn-base"
79/* Number of characters in a pristine file basename, in WC format <= 28. */
80#define PRISTINE_BASENAME_OLD_LEN 40
81#define SDB_FILE  "wc.db"
82
83
84/* Read the properties from the file at PROPFILE_ABSPATH, returning them
85   as a hash in *PROPS. If the propfile is NOT present, then NULL will
86   be returned in *PROPS.  */
87static svn_error_t *
88read_propfile(apr_hash_t **props,
89              const char *propfile_abspath,
90              apr_pool_t *result_pool,
91              apr_pool_t *scratch_pool)
92{
93  svn_error_t *err;
94  svn_stream_t *stream;
95  apr_finfo_t finfo;
96
97  err = svn_io_stat(&finfo, propfile_abspath, APR_FINFO_SIZE, scratch_pool);
98
99  if (err
100      && (APR_STATUS_IS_ENOENT(err->apr_err)
101          || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
102    {
103      svn_error_clear(err);
104
105      /* The propfile was not there. Signal with a NULL.  */
106      *props = NULL;
107      return SVN_NO_ERROR;
108    }
109  else
110    SVN_ERR(err);
111
112  /* A 0-bytes file signals an empty property list.
113     (mostly used for revert-props) */
114  if (finfo.size == 0)
115    {
116      *props = apr_hash_make(result_pool);
117      return SVN_NO_ERROR;
118    }
119
120  SVN_ERR(svn_stream_open_readonly(&stream, propfile_abspath,
121                                   scratch_pool, scratch_pool));
122
123  /* ### does this function need to be smarter? will we see zero-length
124     ### files? see props.c::load_props(). there may be more work here.
125     ### need a historic analysis of 1.x property storage. what will we
126     ### actually run into?  */
127
128  /* ### loggy_write_properties() and immediate_install_props() write
129     ### zero-length files for "no props", so we should be a bit smarter
130     ### in here.  */
131
132  /* ### should we be forgiving in here? I say "no". if we can't be sure,
133     ### then we could effectively corrupt the local working copy.  */
134
135  *props = apr_hash_make(result_pool);
136  SVN_ERR(svn_hash_read2(*props, stream, SVN_HASH_TERMINATOR, result_pool));
137
138  return svn_error_trace(svn_stream_close(stream));
139}
140
141
142/* Read one proplist (allocated from RESULT_POOL) from STREAM, and place it
143   into ALL_WCPROPS at NAME.  */
144static svn_error_t *
145read_one_proplist(apr_hash_t *all_wcprops,
146                  const char *name,
147                  svn_stream_t *stream,
148                  apr_pool_t *result_pool,
149                  apr_pool_t *scratch_pool)
150{
151  apr_hash_t *proplist;
152
153  proplist = apr_hash_make(result_pool);
154  SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, result_pool));
155  svn_hash_sets(all_wcprops, name, proplist);
156
157  return SVN_NO_ERROR;
158}
159
160
161/* Read the wcprops from all the files in the admin area of DIR_ABSPATH,
162   returning them in *ALL_WCPROPS. Results are allocated in RESULT_POOL,
163   and temporary allocations are performed in SCRATCH_POOL.  */
164static svn_error_t *
165read_many_wcprops(apr_hash_t **all_wcprops,
166                  const char *dir_abspath,
167                  apr_pool_t *result_pool,
168                  apr_pool_t *scratch_pool)
169{
170  const char *propfile_abspath;
171  apr_hash_t *wcprops;
172  apr_hash_t *dirents;
173  const char *props_dir_abspath;
174  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
175  apr_hash_index_t *hi;
176
177  *all_wcprops = apr_hash_make(result_pool);
178
179  /* First, look at dir-wcprops. */
180  propfile_abspath = svn_wc__adm_child(dir_abspath, WCPROPS_FNAME_FOR_DIR,
181                                       scratch_pool);
182  SVN_ERR(read_propfile(&wcprops, propfile_abspath, result_pool, iterpool));
183  if (wcprops != NULL)
184    svn_hash_sets(*all_wcprops, SVN_WC_ENTRY_THIS_DIR, wcprops);
185
186  props_dir_abspath = svn_wc__adm_child(dir_abspath, WCPROPS_SUBDIR_FOR_FILES,
187                                        scratch_pool);
188
189  /* Now walk the wcprops directory. */
190  SVN_ERR(svn_io_get_dirents3(&dirents, props_dir_abspath, TRUE,
191                              scratch_pool, scratch_pool));
192
193  for (hi = apr_hash_first(scratch_pool, dirents);
194       hi;
195       hi = apr_hash_next(hi))
196    {
197      const char *name = apr_hash_this_key(hi);
198
199      svn_pool_clear(iterpool);
200
201      propfile_abspath = svn_dirent_join(props_dir_abspath, name, iterpool);
202
203      SVN_ERR(read_propfile(&wcprops, propfile_abspath,
204                            result_pool, iterpool));
205      SVN_ERR_ASSERT(wcprops != NULL);
206      svn_hash_sets(*all_wcprops, apr_pstrdup(result_pool, name), wcprops);
207    }
208
209  svn_pool_destroy(iterpool);
210  return SVN_NO_ERROR;
211}
212
213
214/* For wcprops stored in a single file in this working copy, read that
215   file and return it in *ALL_WCPROPS, allocated in RESULT_POOL.   Use
216   SCRATCH_POOL for temporary allocations. */
217static svn_error_t *
218read_wcprops(apr_hash_t **all_wcprops,
219             const char *dir_abspath,
220             apr_pool_t *result_pool,
221             apr_pool_t *scratch_pool)
222{
223  svn_stream_t *stream;
224  svn_error_t *err;
225
226  *all_wcprops = apr_hash_make(result_pool);
227
228  err = svn_wc__open_adm_stream(&stream, dir_abspath,
229                                WCPROPS_ALL_DATA,
230                                scratch_pool, scratch_pool);
231
232  /* A non-existent file means there are no props. */
233  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
234    {
235      svn_error_clear(err);
236      return SVN_NO_ERROR;
237    }
238  SVN_ERR(err);
239
240  /* Read the proplist for THIS_DIR. */
241  SVN_ERR(read_one_proplist(*all_wcprops, SVN_WC_ENTRY_THIS_DIR, stream,
242                            result_pool, scratch_pool));
243
244  /* And now, the children. */
245  while (1729)
246    {
247      svn_stringbuf_t *line;
248      svn_boolean_t eof;
249
250      SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, result_pool));
251      if (eof)
252        {
253          if (line->len > 0)
254            return svn_error_createf
255              (SVN_ERR_WC_CORRUPT, NULL,
256               _("Missing end of line in wcprops file for '%s'"),
257               svn_dirent_local_style(dir_abspath, scratch_pool));
258          break;
259        }
260      SVN_ERR(read_one_proplist(*all_wcprops, line->data, stream,
261                                result_pool, scratch_pool));
262    }
263
264  return svn_error_trace(svn_stream_close(stream));
265}
266
267/* Return in CHILDREN, the list of all 1.6 versioned subdirectories
268   which also exist on disk as directories.
269
270   If DELETE_DIR is not NULL set *DELETE_DIR to TRUE if the directory
271   should be deleted after migrating to WC-NG, otherwise to FALSE.
272
273   If SKIP_MISSING is TRUE, don't add missing or obstructed subdirectories
274   to the list of children.
275   */
276static svn_error_t *
277get_versioned_subdirs(apr_array_header_t **children,
278                      svn_boolean_t *delete_dir,
279                      const char *dir_abspath,
280                      svn_boolean_t skip_missing,
281                      apr_pool_t *result_pool,
282                      apr_pool_t *scratch_pool)
283{
284  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
285  apr_hash_t *entries;
286  apr_hash_index_t *hi;
287  svn_wc_entry_t *this_dir = NULL;
288
289  *children = apr_array_make(result_pool, 10, sizeof(const char *));
290
291  SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath,
292                                   scratch_pool, iterpool));
293  for (hi = apr_hash_first(scratch_pool, entries);
294       hi;
295       hi = apr_hash_next(hi))
296    {
297      const char *name = apr_hash_this_key(hi);
298      const svn_wc_entry_t *entry = apr_hash_this_val(hi);
299      const char *child_abspath;
300      svn_boolean_t hidden;
301
302      /* skip "this dir"  */
303      if (*name == '\0')
304        {
305          this_dir = apr_hash_this_val(hi);
306          continue;
307        }
308      else if (entry->kind != svn_node_dir)
309        continue;
310
311      svn_pool_clear(iterpool);
312
313      /* If a directory is 'hidden' skip it as subdir */
314      SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry));
315      if (hidden)
316        continue;
317
318      child_abspath = svn_dirent_join(dir_abspath, name, scratch_pool);
319
320      if (skip_missing)
321        {
322          svn_node_kind_t kind;
323          SVN_ERR(svn_io_check_path(child_abspath, &kind, scratch_pool));
324
325          if (kind != svn_node_dir)
326            continue;
327        }
328
329      APR_ARRAY_PUSH(*children, const char *) = apr_pstrdup(result_pool,
330                                                            child_abspath);
331    }
332
333  svn_pool_destroy(iterpool);
334
335  if (delete_dir != NULL)
336    {
337      *delete_dir = (this_dir != NULL)
338                     && (this_dir->schedule == svn_wc_schedule_delete)
339                     && ! this_dir->keep_local;
340    }
341
342  return SVN_NO_ERROR;
343}
344
345
346/* Return in CHILDREN the names of all versioned *files* in SDB that
347   are children of PARENT_RELPATH.  These files' existence on disk is
348   not tested.
349
350   This set of children is intended for property upgrades.
351   Subdirectory's properties exist in the subdirs.
352
353   Note that this uses just the SDB to locate children, which means
354   that the children must have been upgraded to wc-ng format. */
355static svn_error_t *
356get_versioned_files(const apr_array_header_t **children,
357                    const char *parent_relpath,
358                    svn_sqlite__db_t *sdb,
359                    apr_int64_t wc_id,
360                    apr_pool_t *result_pool,
361                    apr_pool_t *scratch_pool)
362{
363  svn_sqlite__stmt_t *stmt;
364  apr_array_header_t *child_names;
365  svn_boolean_t have_row;
366
367  /* ### just select 'file' children. do we need 'symlink' in the future?  */
368  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ALL_FILES));
369  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
370
371  /* ### 10 is based on Subversion's average of 8.5 files per versioned
372     ### directory in its repository. maybe use a different value? or
373     ### count rows first?  */
374  child_names = apr_array_make(result_pool, 10, sizeof(const char *));
375
376  SVN_ERR(svn_sqlite__step(&have_row, stmt));
377  while (have_row)
378    {
379      const char *local_relpath = svn_sqlite__column_text(stmt, 0,
380                                                          result_pool);
381
382      APR_ARRAY_PUSH(child_names, const char *)
383        = svn_relpath_basename(local_relpath, result_pool);
384
385      SVN_ERR(svn_sqlite__step(&have_row, stmt));
386    }
387
388  *children = child_names;
389
390  return svn_error_trace(svn_sqlite__reset(stmt));
391}
392
393
394/* Return the path of the old-school administrative lock file
395   associated with LOCAL_DIR_ABSPATH, allocated from RESULT_POOL. */
396static const char *
397build_lockfile_path(const char *local_dir_abspath,
398                    apr_pool_t *result_pool)
399{
400  return svn_dirent_join_many(result_pool,
401                              local_dir_abspath,
402                              svn_wc_get_adm_dir(result_pool),
403                              ADM_LOCK,
404                              SVN_VA_NULL);
405}
406
407
408/* Create a physical lock file in the admin directory for ABSPATH.  */
409static svn_error_t *
410create_physical_lock(const char *abspath, apr_pool_t *scratch_pool)
411{
412  const char *lock_abspath = build_lockfile_path(abspath, scratch_pool);
413  svn_error_t *err;
414  apr_file_t *file;
415
416  err = svn_io_file_open(&file, lock_abspath,
417                         APR_WRITE | APR_CREATE | APR_EXCL,
418                         APR_OS_DEFAULT,
419                         scratch_pool);
420
421  if (err && APR_STATUS_IS_EEXIST(err->apr_err))
422    {
423      /* Congratulations, we just stole a physical lock from somebody */
424      svn_error_clear(err);
425      return SVN_NO_ERROR;
426    }
427
428  return svn_error_trace(err);
429}
430
431
432/* Wipe out all the obsolete files/dirs from the administrative area.  */
433static void
434wipe_obsolete_files(const char *wcroot_abspath, apr_pool_t *scratch_pool)
435{
436  /* Zap unused files.  */
437  svn_error_clear(svn_io_remove_file2(
438                    svn_wc__adm_child(wcroot_abspath,
439                                      SVN_WC__ADM_FORMAT,
440                                      scratch_pool),
441                    TRUE, scratch_pool));
442  svn_error_clear(svn_io_remove_file2(
443                    svn_wc__adm_child(wcroot_abspath,
444                                      SVN_WC__ADM_ENTRIES,
445                                      scratch_pool),
446                    TRUE, scratch_pool));
447  svn_error_clear(svn_io_remove_file2(
448                    svn_wc__adm_child(wcroot_abspath,
449                                      ADM_EMPTY_FILE,
450                                      scratch_pool),
451                    TRUE, scratch_pool));
452  svn_error_clear(svn_io_remove_file2(
453                    svn_wc__adm_child(wcroot_abspath,
454                                      ADM_README,
455                                      scratch_pool),
456                    TRUE, scratch_pool));
457
458  /* For formats <= SVN_WC__WCPROPS_MANY_FILES_VERSION, we toss the wcprops
459     for the directory itself, and then all the wcprops for the files.  */
460  svn_error_clear(svn_io_remove_file2(
461                    svn_wc__adm_child(wcroot_abspath,
462                                      WCPROPS_FNAME_FOR_DIR,
463                                      scratch_pool),
464                    TRUE, scratch_pool));
465  svn_error_clear(svn_io_remove_dir2(
466                    svn_wc__adm_child(wcroot_abspath,
467                                      WCPROPS_SUBDIR_FOR_FILES,
468                                      scratch_pool),
469                    FALSE, NULL, NULL, scratch_pool));
470
471  /* And for later formats, they are aggregated into one file.  */
472  svn_error_clear(svn_io_remove_file2(
473                    svn_wc__adm_child(wcroot_abspath,
474                                      WCPROPS_ALL_DATA,
475                                      scratch_pool),
476                    TRUE, scratch_pool));
477
478  /* Remove the old text-base directory and the old text-base files. */
479  svn_error_clear(svn_io_remove_dir2(
480                    svn_wc__adm_child(wcroot_abspath,
481                                      TEXT_BASE_SUBDIR,
482                                      scratch_pool),
483                    FALSE, NULL, NULL, scratch_pool));
484
485  /* Remove the old properties files... whole directories at a time.  */
486  svn_error_clear(svn_io_remove_dir2(
487                    svn_wc__adm_child(wcroot_abspath,
488                                      PROPS_SUBDIR,
489                                      scratch_pool),
490                    FALSE, NULL, NULL, scratch_pool));
491  svn_error_clear(svn_io_remove_dir2(
492                    svn_wc__adm_child(wcroot_abspath,
493                                      PROP_BASE_SUBDIR,
494                                      scratch_pool),
495                    FALSE, NULL, NULL, scratch_pool));
496  svn_error_clear(svn_io_remove_file2(
497                     svn_wc__adm_child(wcroot_abspath,
498                                       PROP_WORKING_FOR_DIR,
499                                       scratch_pool),
500                     TRUE, scratch_pool));
501  svn_error_clear(svn_io_remove_file2(
502                     svn_wc__adm_child(wcroot_abspath,
503                                      PROP_BASE_FOR_DIR,
504                                      scratch_pool),
505                     TRUE, scratch_pool));
506  svn_error_clear(svn_io_remove_file2(
507                     svn_wc__adm_child(wcroot_abspath,
508                                      PROP_REVERT_FOR_DIR,
509                                      scratch_pool),
510                     TRUE, scratch_pool));
511
512#if 0
513  /* ### this checks for a write-lock, and we are not (always) taking out
514     ### a write lock in all callers.  */
515  SVN_ERR(svn_wc__adm_cleanup_tmp_area(db, wcroot_abspath, iterpool));
516#endif
517
518  /* Remove the old-style lock file LAST.  */
519  svn_error_clear(svn_io_remove_file2(
520                    build_lockfile_path(wcroot_abspath, scratch_pool),
521                    TRUE, scratch_pool));
522}
523
524svn_error_t *
525svn_wc__wipe_postupgrade(const char *dir_abspath,
526                         svn_boolean_t whole_admin,
527                         svn_cancel_func_t cancel_func,
528                         void *cancel_baton,
529                         apr_pool_t *scratch_pool)
530{
531  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
532  apr_array_header_t *subdirs;
533  svn_error_t *err;
534  svn_boolean_t delete_dir;
535  int i;
536
537  if (cancel_func)
538    SVN_ERR((*cancel_func)(cancel_baton));
539
540  err = get_versioned_subdirs(&subdirs, &delete_dir, dir_abspath, TRUE,
541                              scratch_pool, iterpool);
542  if (err)
543    {
544      if (APR_STATUS_IS_ENOENT(err->apr_err))
545        {
546          /* An unversioned dir is obstructing a versioned dir */
547          svn_error_clear(err);
548          err = NULL;
549        }
550      svn_pool_destroy(iterpool);
551      return svn_error_trace(err);
552    }
553  for (i = 0; i < subdirs->nelts; ++i)
554    {
555      const char *child_abspath = APR_ARRAY_IDX(subdirs, i, const char *);
556
557      svn_pool_clear(iterpool);
558      SVN_ERR(svn_wc__wipe_postupgrade(child_abspath, TRUE,
559                                       cancel_func, cancel_baton, iterpool));
560    }
561
562  /* ### Should we really be ignoring errors here? */
563  if (whole_admin)
564    svn_error_clear(svn_io_remove_dir2(svn_wc__adm_child(dir_abspath, "",
565                                                         iterpool),
566                                       TRUE, NULL, NULL, iterpool));
567  else
568    wipe_obsolete_files(dir_abspath, scratch_pool);
569
570  if (delete_dir)
571    {
572      /* If this was a WC-NG single database copy, this directory wouldn't
573         be here (unless it was deleted with --keep-local)
574
575         If the directory is empty, we can just delete it; if not we
576         keep it.
577       */
578      svn_error_clear(svn_io_dir_remove_nonrecursive(dir_abspath, iterpool));
579    }
580
581  svn_pool_destroy(iterpool);
582
583  return SVN_NO_ERROR;
584}
585
586/* Ensure that ENTRY has its REPOS and UUID fields set. These will be
587   used to establish the REPOSITORY row in the new database, and then
588   used within the upgraded entries as they are written into the database.
589
590   If one or both are not available, then it attempts to retrieve this
591   information from REPOS_CACHE. And if that fails from REPOS_INFO_FUNC,
592   passing REPOS_INFO_BATON.
593   Returns a user understandable error using LOCAL_ABSPATH if the
594   information cannot be obtained.  */
595static svn_error_t *
596ensure_repos_info(svn_wc_entry_t *entry,
597                  const char *local_abspath,
598                  svn_wc_upgrade_get_repos_info_t repos_info_func,
599                  void *repos_info_baton,
600                  apr_hash_t *repos_cache,
601                  apr_pool_t *result_pool,
602                  apr_pool_t *scratch_pool)
603{
604  /* Easy exit.  */
605  if (entry->repos != NULL && entry->uuid != NULL)
606    return SVN_NO_ERROR;
607
608  if ((entry->repos == NULL || entry->uuid == NULL)
609      && entry->url)
610    {
611      apr_hash_index_t *hi;
612
613      for (hi = apr_hash_first(scratch_pool, repos_cache);
614           hi; hi = apr_hash_next(hi))
615        {
616          if (svn_uri__is_ancestor(apr_hash_this_key(hi), entry->url))
617            {
618              if (!entry->repos)
619                entry->repos = apr_hash_this_key(hi);
620
621              if (!entry->uuid)
622                entry->uuid = apr_hash_this_val(hi);
623
624              return SVN_NO_ERROR;
625            }
626        }
627    }
628
629  if (entry->repos == NULL && repos_info_func == NULL)
630    return svn_error_createf(
631        SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
632        _("Working copy '%s' can't be upgraded because the repository root is "
633          "not available and can't be retrieved"),
634        svn_dirent_local_style(local_abspath, scratch_pool));
635
636  if (entry->uuid == NULL && repos_info_func == NULL)
637    return svn_error_createf(
638        SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
639        _("Working copy '%s' can't be upgraded because the repository uuid is "
640          "not available and can't be retrieved"),
641        svn_dirent_local_style(local_abspath, scratch_pool));
642
643   if (entry->url == NULL)
644     return svn_error_createf(
645        SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
646        _("Working copy '%s' can't be upgraded because it doesn't have a url"),
647        svn_dirent_local_style(local_abspath, scratch_pool));
648
649   return svn_error_trace((*repos_info_func)(&entry->repos, &entry->uuid,
650                                             repos_info_baton,
651                                             entry->url,
652                                             result_pool, scratch_pool));
653}
654
655
656/*
657 * Read tree conflict descriptions from @a conflict_data.  Set @a *conflicts
658 * to a hash of pointers to svn_wc_conflict_description2_t objects indexed by
659 * svn_wc_conflict_description2_t.local_abspath, all newly allocated in @a
660 * pool.  @a dir_path is the path to the working copy directory whose conflicts
661 * are being read.  The conflicts read are the tree conflicts on the immediate
662 * child nodes of @a dir_path.  Do all allocations in @a pool.
663 *
664 * Note: There were some concerns about this function:
665 *
666 * ### this is BAD. the CONFLICTS structure should not be dependent upon
667 * ### DIR_PATH. each conflict should be labeled with an entry name, not
668 * ### a whole path. (and a path which happens to vary based upon invocation
669 * ### of the user client and these APIs)
670 *
671 * those assumptions were baked into former versions of the data model, so
672 * they have to stick around here.  But they have been removed from the
673 * New Way. */
674static svn_error_t *
675read_tree_conflicts(apr_hash_t **conflicts,
676                    const char *conflict_data,
677                    const char *dir_path,
678                    apr_pool_t *pool)
679{
680  const svn_skel_t *skel;
681  apr_pool_t *iterpool;
682
683  *conflicts = apr_hash_make(pool);
684
685  if (conflict_data == NULL)
686    return SVN_NO_ERROR;
687
688  skel = svn_skel__parse(conflict_data, strlen(conflict_data), pool);
689  if (skel == NULL)
690    return svn_error_create(SVN_ERR_WC_CORRUPT, NULL,
691                            _("Error parsing tree conflict skel"));
692
693  iterpool = svn_pool_create(pool);
694  for (skel = skel->children; skel != NULL; skel = skel->next)
695    {
696      const svn_wc_conflict_description2_t *conflict;
697
698      svn_pool_clear(iterpool);
699      SVN_ERR(svn_wc__deserialize_conflict(&conflict, skel, dir_path,
700                                           pool, iterpool));
701      if (conflict != NULL)
702        svn_hash_sets(*conflicts,
703                      svn_dirent_basename(conflict->local_abspath, pool),
704                      conflict);
705    }
706  svn_pool_destroy(iterpool);
707
708  return SVN_NO_ERROR;
709}
710
711/* */
712static svn_error_t *
713migrate_single_tree_conflict_data(svn_sqlite__db_t *sdb,
714                                  const char *tree_conflict_data,
715                                  apr_int64_t wc_id,
716                                  const char *local_relpath,
717                                  apr_pool_t *scratch_pool)
718{
719  apr_hash_t *conflicts;
720  apr_hash_index_t *hi;
721  apr_pool_t *iterpool;
722
723  SVN_ERR(read_tree_conflicts(&conflicts, tree_conflict_data, local_relpath,
724                              scratch_pool));
725
726  iterpool = svn_pool_create(scratch_pool);
727  for (hi = apr_hash_first(scratch_pool, conflicts);
728       hi;
729       hi = apr_hash_next(hi))
730    {
731      const svn_wc_conflict_description2_t *conflict = apr_hash_this_val(hi);
732      const char *conflict_relpath;
733      const char *conflict_data;
734      svn_sqlite__stmt_t *stmt;
735      svn_boolean_t have_row;
736      svn_skel_t *skel;
737
738      svn_pool_clear(iterpool);
739
740      conflict_relpath = svn_dirent_join(local_relpath,
741                                         svn_dirent_basename(
742                                           conflict->local_abspath, iterpool),
743                                         iterpool);
744
745      SVN_ERR(svn_wc__serialize_conflict(&skel, conflict, iterpool, iterpool));
746      conflict_data = svn_skel__unparse(skel, iterpool)->data;
747
748      /* See if we need to update or insert an ACTUAL node. */
749      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ACTUAL_NODE));
750      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, conflict_relpath));
751      SVN_ERR(svn_sqlite__step(&have_row, stmt));
752      SVN_ERR(svn_sqlite__reset(stmt));
753
754      if (have_row)
755        {
756          /* There is an existing ACTUAL row, so just update it. */
757          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
758                                            STMT_UPDATE_ACTUAL_CONFLICT));
759        }
760      else
761        {
762          /* We need to insert an ACTUAL row with the tree conflict data. */
763          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
764                                            STMT_INSERT_ACTUAL_CONFLICT));
765        }
766
767      SVN_ERR(svn_sqlite__bindf(stmt, "iss", wc_id, conflict_relpath,
768                                conflict_data));
769      if (!have_row)
770        SVN_ERR(svn_sqlite__bind_text(stmt, 4, local_relpath));
771
772      SVN_ERR(svn_sqlite__step_done(stmt));
773    }
774
775  svn_pool_destroy(iterpool);
776
777  return SVN_NO_ERROR;
778}
779
780
781/* */
782static svn_error_t *
783migrate_tree_conflict_data(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
784{
785  svn_sqlite__stmt_t *stmt;
786  svn_boolean_t have_row;
787  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
788
789  /* Iterate over each node which has a set of tree conflicts, then insert
790     all of them into the new schema.  */
791
792  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
793                                    STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT));
794
795  /* Get all the existing tree conflict data. */
796  SVN_ERR(svn_sqlite__step(&have_row, stmt));
797  while (have_row)
798    {
799      apr_int64_t wc_id;
800      const char *local_relpath;
801      const char *tree_conflict_data;
802
803      svn_pool_clear(iterpool);
804
805      wc_id = svn_sqlite__column_int64(stmt, 0);
806      local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
807      tree_conflict_data = svn_sqlite__column_text(stmt, 2, iterpool);
808
809      SVN_ERR(migrate_single_tree_conflict_data(sdb, tree_conflict_data,
810                                                wc_id, local_relpath,
811                                                iterpool));
812
813      /* We don't need to do anything but step over the previously
814         prepared statement. */
815      SVN_ERR(svn_sqlite__step(&have_row, stmt));
816    }
817  SVN_ERR(svn_sqlite__reset(stmt));
818
819  /* Erase all the old tree conflict data.  */
820  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
821                                    STMT_UPGRADE_21_ERASE_OLD_CONFLICTS));
822  SVN_ERR(svn_sqlite__step_done(stmt));
823
824  svn_pool_destroy(iterpool);
825  return SVN_NO_ERROR;
826}
827
828/* ### need much more docco
829
830   ### this function should be called within a sqlite transaction. it makes
831   ### assumptions around this fact.
832
833   Apply the various sets of properties to the database nodes based on
834   their existence/presence, the current state of the node, and the original
835   format of the working copy which provided these property sets.
836*/
837static svn_error_t *
838upgrade_apply_props(svn_sqlite__db_t *sdb,
839                    const char *dir_abspath,
840                    const char *local_relpath,
841                    apr_hash_t *base_props,
842                    apr_hash_t *revert_props,
843                    apr_hash_t *working_props,
844                    int original_format,
845                    apr_int64_t wc_id,
846                    apr_pool_t *scratch_pool)
847{
848  svn_sqlite__stmt_t *stmt;
849  svn_boolean_t have_row;
850  int top_op_depth = -1;
851  int below_op_depth = -1;
852  svn_wc__db_status_t top_presence;
853  svn_wc__db_status_t below_presence;
854  int affected_rows;
855
856  /* ### working_props: use set_props_txn.
857     ### if working_props == NULL, then skip. what if they equal the
858     ### pristine props? we should probably do the compare here.
859     ###
860     ### base props go into WORKING_NODE if avail, otherwise BASE.
861     ###
862     ### revert only goes into BASE. (and WORKING better be there!)
863
864     Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
865     file was deleted, then a copy (potentially with props) was disallowed
866     and could not replace the deletion. An addition *could* be performed,
867     but that would never bring its own props.
868
869     1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
870     bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
871     construct a REVERT_PROPS if the target had no props. Thus, reverting
872     the delete/copy would see no REVERT_PROPS to restore, leaving the
873     props from the copy source intact, and appearing as if they are (now)
874     the base props for the previously-deleted file. (wc corruption)
875
876     1.4.6 ensured that an empty REVERT_PROPS would be established at all
877     times. See issue 2530, and r861670 as starting points.
878
879     We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
880     the handling of our inputs, relative to the state of this node.
881  */
882
883  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
884  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
885  SVN_ERR(svn_sqlite__step(&have_row, stmt));
886  if (have_row)
887    {
888      top_op_depth = svn_sqlite__column_int(stmt, 0);
889      top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
890      SVN_ERR(svn_sqlite__step(&have_row, stmt));
891      if (have_row)
892        {
893          below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
894
895          /* There might be an intermediate layer on mixed-revision copies,
896             or when BASE is shadowed */
897          if (below_presence == svn_wc__db_status_not_present
898              || below_presence == svn_wc__db_status_deleted)
899            SVN_ERR(svn_sqlite__step(&have_row, stmt));
900
901          if (have_row)
902            {
903              below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
904              below_op_depth = svn_sqlite__column_int(stmt, 0);
905            }
906        }
907    }
908  SVN_ERR(svn_sqlite__reset(stmt));
909
910  /* Detect the buggy scenario described above. We cannot upgrade this
911     working copy if we have no idea where BASE_PROPS should go.  */
912  if (original_format > SVN_WC__NO_REVERT_FILES
913      && revert_props == NULL
914      && top_op_depth != -1
915      && top_presence == svn_wc__db_status_normal
916      && below_op_depth != -1
917      && below_presence != svn_wc__db_status_not_present)
918    {
919      /* There should be REVERT_PROPS, so it appears that we just ran into
920         the described bug. Sigh.  */
921      return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
922                               _("The properties of '%s' are in an "
923                                 "indeterminate state and cannot be "
924                                 "upgraded. See issue #2530."),
925                               svn_dirent_local_style(
926                                 svn_dirent_join(dir_abspath, local_relpath,
927                                                 scratch_pool), scratch_pool));
928    }
929
930  /* Need at least one row, or two rows if there are revert props */
931  if (top_op_depth == -1
932      || (below_op_depth == -1 && revert_props))
933    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
934                             _("Insufficient NODES rows for '%s'"),
935                             svn_dirent_local_style(
936                               svn_dirent_join(dir_abspath, local_relpath,
937                                               scratch_pool), scratch_pool));
938
939  /* one row, base props only: upper row gets base props
940     two rows, base props only: lower row gets base props
941     two rows, revert props only: lower row gets revert props
942     two rows, base and revert props: upper row gets base, lower gets revert */
943
944
945  if (revert_props || below_op_depth == -1)
946    {
947      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
948                                        STMT_UPDATE_NODE_PROPS));
949      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
950                                wc_id, local_relpath, top_op_depth));
951      SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
952      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
953
954      SVN_ERR_ASSERT(affected_rows == 1);
955    }
956
957  if (below_op_depth != -1)
958    {
959      apr_hash_t *props = revert_props ? revert_props : base_props;
960
961      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
962                                        STMT_UPDATE_NODE_PROPS));
963      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
964                                wc_id, local_relpath, below_op_depth));
965      SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
966      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
967
968      SVN_ERR_ASSERT(affected_rows == 1);
969    }
970
971  /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE.  */
972  if (working_props != NULL
973      && base_props != NULL)
974    {
975      apr_array_header_t *diffs;
976
977      SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
978
979      if (diffs->nelts == 0)
980        working_props = NULL; /* No differences */
981    }
982
983  if (working_props != NULL)
984    {
985      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
986                                  STMT_UPDATE_ACTUAL_PROPS));
987      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
988      SVN_ERR(svn_sqlite__bind_properties(stmt, 3, working_props,
989                                          scratch_pool));
990      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
991
992      if (affected_rows == 0)
993        {
994          /* We have to insert a row in ACTUAL */
995
996          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
997                                            STMT_INSERT_ACTUAL_PROPS));
998          SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
999          if (*local_relpath != '\0')
1000            SVN_ERR(svn_sqlite__bind_text(stmt, 3,
1001                                          svn_relpath_dirname(local_relpath,
1002                                                              scratch_pool)));
1003          SVN_ERR(svn_sqlite__bind_properties(stmt, 4, working_props,
1004                                              scratch_pool));
1005          return svn_error_trace(svn_sqlite__step_done(stmt));
1006        }
1007    }
1008
1009  return SVN_NO_ERROR;
1010}
1011
1012
1013struct bump_baton {
1014  const char *wcroot_abspath;
1015};
1016
1017/* Migrate the properties for one node (LOCAL_ABSPATH).  */
1018static svn_error_t *
1019migrate_node_props(const char *dir_abspath,
1020                   const char *new_wcroot_abspath,
1021                   const char *name,
1022                   svn_sqlite__db_t *sdb,
1023                   int original_format,
1024                   apr_int64_t wc_id,
1025                   apr_pool_t *scratch_pool)
1026{
1027  const char *base_abspath;  /* old name. nowadays: "pristine"  */
1028  const char *revert_abspath;  /* old name. nowadays: "BASE"  */
1029  const char *working_abspath;  /* old name. nowadays: "ACTUAL"  */
1030  apr_hash_t *base_props;
1031  apr_hash_t *revert_props;
1032  apr_hash_t *working_props;
1033  const char *old_wcroot_abspath
1034    = svn_dirent_get_longest_ancestor(dir_abspath, new_wcroot_abspath,
1035                                      scratch_pool);
1036  const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath,
1037                                                     dir_abspath);
1038
1039  if (*name == '\0')
1040    {
1041      base_abspath = svn_wc__adm_child(dir_abspath,
1042                                       PROP_BASE_FOR_DIR, scratch_pool);
1043      revert_abspath = svn_wc__adm_child(dir_abspath,
1044                                         PROP_REVERT_FOR_DIR, scratch_pool);
1045      working_abspath = svn_wc__adm_child(dir_abspath,
1046                                          PROP_WORKING_FOR_DIR, scratch_pool);
1047    }
1048  else
1049    {
1050      const char *basedir_abspath;
1051      const char *propsdir_abspath;
1052
1053      propsdir_abspath = svn_wc__adm_child(dir_abspath, PROPS_SUBDIR,
1054                                           scratch_pool);
1055      basedir_abspath = svn_wc__adm_child(dir_abspath, PROP_BASE_SUBDIR,
1056                                          scratch_pool);
1057
1058      base_abspath = svn_dirent_join(basedir_abspath,
1059                                     apr_pstrcat(scratch_pool,
1060                                                 name,
1061                                                 SVN_WC__BASE_EXT,
1062                                                 SVN_VA_NULL),
1063                                     scratch_pool);
1064
1065      revert_abspath = svn_dirent_join(basedir_abspath,
1066                                       apr_pstrcat(scratch_pool,
1067                                                   name,
1068                                                   SVN_WC__REVERT_EXT,
1069                                                   SVN_VA_NULL),
1070                                       scratch_pool);
1071
1072      working_abspath = svn_dirent_join(propsdir_abspath,
1073                                        apr_pstrcat(scratch_pool,
1074                                                    name,
1075                                                    SVN_WC__WORK_EXT,
1076                                                    SVN_VA_NULL),
1077                                        scratch_pool);
1078    }
1079
1080  SVN_ERR(read_propfile(&base_props, base_abspath,
1081                        scratch_pool, scratch_pool));
1082  SVN_ERR(read_propfile(&revert_props, revert_abspath,
1083                        scratch_pool, scratch_pool));
1084  SVN_ERR(read_propfile(&working_props, working_abspath,
1085                        scratch_pool, scratch_pool));
1086
1087  return svn_error_trace(upgrade_apply_props(
1088                            sdb, new_wcroot_abspath,
1089                            svn_relpath_join(dir_relpath, name, scratch_pool),
1090                            base_props, revert_props, working_props,
1091                            original_format, wc_id,
1092                            scratch_pool));
1093}
1094
1095
1096/* */
1097static svn_error_t *
1098migrate_props(const char *dir_abspath,
1099              const char *new_wcroot_abspath,
1100              svn_sqlite__db_t *sdb,
1101              int original_format,
1102              apr_int64_t wc_id,
1103              apr_pool_t *scratch_pool)
1104{
1105  /* General logic here: iterate over all the immediate children of the root
1106     (since we aren't yet in a centralized system), and for any properties that
1107     exist, map them as follows:
1108
1109     if (revert props exist):
1110       revert  -> BASE
1111       base    -> WORKING
1112       working -> ACTUAL
1113     else if (prop pristine is working [as defined in props.c] ):
1114       base    -> WORKING
1115       working -> ACTUAL
1116     else:
1117       base    -> BASE
1118       working -> ACTUAL
1119
1120     ### the middle "test" should simply look for a WORKING_NODE row
1121
1122     Note that it is legal for "working" props to be missing. That implies
1123     no local changes to the properties.
1124  */
1125  const apr_array_header_t *children;
1126  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1127  const char *old_wcroot_abspath
1128    = svn_dirent_get_longest_ancestor(dir_abspath, new_wcroot_abspath,
1129                                      scratch_pool);
1130  const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath,
1131                                                     dir_abspath);
1132  int i;
1133
1134  /* Migrate the props for "this dir".  */
1135  SVN_ERR(migrate_node_props(dir_abspath, new_wcroot_abspath, "", sdb,
1136                             original_format, wc_id, iterpool));
1137
1138  /* Iterate over all the files in this SDB.  */
1139  SVN_ERR(get_versioned_files(&children, dir_relpath, sdb, wc_id, scratch_pool,
1140                              iterpool));
1141  for (i = 0; i < children->nelts; i++)
1142    {
1143      const char *name = APR_ARRAY_IDX(children, i, const char *);
1144
1145      svn_pool_clear(iterpool);
1146
1147      SVN_ERR(migrate_node_props(dir_abspath, new_wcroot_abspath,
1148                                 name, sdb, original_format, wc_id, iterpool));
1149    }
1150
1151  svn_pool_destroy(iterpool);
1152
1153  return SVN_NO_ERROR;
1154}
1155
1156
1157/* If STR ends with SUFFIX and is longer than SUFFIX, return the part of
1158 * STR that comes before SUFFIX; else return NULL. */
1159static char *
1160remove_suffix(const char *str, const char *suffix, apr_pool_t *result_pool)
1161{
1162  size_t str_len = strlen(str);
1163  size_t suffix_len = strlen(suffix);
1164
1165  if (str_len > suffix_len
1166      && strcmp(str + str_len - suffix_len, suffix) == 0)
1167    {
1168      return apr_pstrmemdup(result_pool, str, str_len - suffix_len);
1169    }
1170
1171  return NULL;
1172}
1173
1174/* Copy all the text-base files from the administrative area of WC directory
1175   DIR_ABSPATH into the pristine store of SDB which is located in directory
1176   NEW_WCROOT_ABSPATH.
1177
1178   Set *TEXT_BASES_INFO to a new hash, allocated in RESULT_POOL, that maps
1179   (const char *) name of the versioned file to (svn_wc__text_base_info_t *)
1180   information about the pristine text. */
1181static svn_error_t *
1182migrate_text_bases(apr_hash_t **text_bases_info,
1183                   const char *dir_abspath,
1184                   const char *new_wcroot_abspath,
1185                   svn_sqlite__db_t *sdb,
1186                   apr_pool_t *result_pool,
1187                   apr_pool_t *scratch_pool)
1188{
1189  apr_hash_t *dirents;
1190  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1191  apr_hash_index_t *hi;
1192  const char *text_base_dir = svn_wc__adm_child(dir_abspath,
1193                                                TEXT_BASE_SUBDIR,
1194                                                scratch_pool);
1195
1196  *text_bases_info = apr_hash_make(result_pool);
1197
1198  /* Iterate over the text-base files */
1199  SVN_ERR(svn_io_get_dirents3(&dirents, text_base_dir, TRUE,
1200                              scratch_pool, scratch_pool));
1201  for (hi = apr_hash_first(scratch_pool, dirents); hi;
1202       hi = apr_hash_next(hi))
1203    {
1204      const char *text_base_basename = apr_hash_this_key(hi);
1205      svn_checksum_t *md5_checksum;
1206      svn_checksum_t *sha1_checksum;
1207
1208      svn_pool_clear(iterpool);
1209
1210      /* Calculate its checksums and copy it to the pristine store */
1211      {
1212        const char *pristine_path;
1213        const char *text_base_path;
1214        const char *temp_path;
1215        svn_sqlite__stmt_t *stmt;
1216        apr_finfo_t finfo;
1217        svn_stream_t *read_stream;
1218        svn_stream_t *result_stream;
1219
1220        text_base_path = svn_dirent_join(text_base_dir, text_base_basename,
1221                                         iterpool);
1222
1223        /* Create a copy and calculate a checksum in one step */
1224        SVN_ERR(svn_stream_open_unique(&result_stream, &temp_path,
1225                                       new_wcroot_abspath,
1226                                       svn_io_file_del_none,
1227                                       iterpool, iterpool));
1228
1229        SVN_ERR(svn_stream_open_readonly(&read_stream, text_base_path,
1230                                           iterpool, iterpool));
1231
1232        read_stream = svn_stream_checksummed2(read_stream, &md5_checksum,
1233                                              NULL, svn_checksum_md5,
1234                                              TRUE, iterpool);
1235
1236        read_stream = svn_stream_checksummed2(read_stream, &sha1_checksum,
1237                                              NULL, svn_checksum_sha1,
1238                                              TRUE, iterpool);
1239
1240        /* This calculates the hash, creates a copy and closes the stream */
1241        SVN_ERR(svn_stream_copy3(read_stream, result_stream,
1242                                 NULL, NULL, iterpool));
1243
1244        SVN_ERR(svn_io_stat(&finfo, text_base_path, APR_FINFO_SIZE, iterpool));
1245
1246        /* Insert a row into the pristine table. */
1247        SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1248                                          STMT_INSERT_OR_IGNORE_PRISTINE));
1249        SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, iterpool));
1250        SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, iterpool));
1251        SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
1252        SVN_ERR(svn_sqlite__insert(NULL, stmt));
1253
1254        SVN_ERR(svn_wc__db_pristine_get_future_path(&pristine_path,
1255                                                    new_wcroot_abspath,
1256                                                    sha1_checksum,
1257                                                    iterpool, iterpool));
1258
1259        /* Ensure any sharding directories exist. */
1260        SVN_ERR(svn_wc__ensure_directory(svn_dirent_dirname(pristine_path,
1261                                                            iterpool),
1262                                         iterpool));
1263
1264        /* Now move the file into the pristine store, overwriting
1265           existing files with the same checksum. */
1266        SVN_ERR(svn_io_file_move(temp_path, pristine_path, iterpool));
1267      }
1268
1269      /* Add the checksums for this text-base to *TEXT_BASES_INFO. */
1270      {
1271        const char *versioned_file_name;
1272        svn_boolean_t is_revert_base;
1273        svn_wc__text_base_info_t *info;
1274        svn_wc__text_base_file_info_t *file_info;
1275
1276        /* Determine the versioned file name and whether this is a normal base
1277         * or a revert base. */
1278        versioned_file_name = remove_suffix(text_base_basename,
1279                                            SVN_WC__REVERT_EXT, result_pool);
1280        if (versioned_file_name)
1281          {
1282            is_revert_base = TRUE;
1283          }
1284        else
1285          {
1286            versioned_file_name = remove_suffix(text_base_basename,
1287                                                SVN_WC__BASE_EXT, result_pool);
1288            is_revert_base = FALSE;
1289          }
1290
1291        if (! versioned_file_name)
1292          {
1293             /* Some file that doesn't end with .svn-base or .svn-revert.
1294                No idea why that would be in our administrative area, but
1295                we shouldn't segfault on this case.
1296
1297                Note that we already copied this file in the pristine store,
1298                but the next cleanup will take care of that.
1299              */
1300            continue;
1301          }
1302
1303        /* Create a new info struct for this versioned file, or fill in the
1304         * existing one if this is the second text-base we've found for it. */
1305        info = svn_hash_gets(*text_bases_info, versioned_file_name);
1306        if (info == NULL)
1307          info = apr_pcalloc(result_pool, sizeof (*info));
1308        file_info = (is_revert_base ? &info->revert_base : &info->normal_base);
1309
1310        file_info->sha1_checksum = svn_checksum_dup(sha1_checksum, result_pool);
1311        file_info->md5_checksum = svn_checksum_dup(md5_checksum, result_pool);
1312        svn_hash_sets(*text_bases_info, versioned_file_name, info);
1313      }
1314    }
1315
1316  svn_pool_destroy(iterpool);
1317
1318  return SVN_NO_ERROR;
1319}
1320
1321static svn_error_t *
1322bump_to_20(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1323{
1324  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_NODES));
1325  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_20));
1326  return SVN_NO_ERROR;
1327}
1328
1329static svn_error_t *
1330bump_to_21(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1331{
1332  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_21));
1333  SVN_ERR(migrate_tree_conflict_data(sdb, scratch_pool));
1334  return SVN_NO_ERROR;
1335}
1336
1337static svn_error_t *
1338bump_to_22(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1339{
1340  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_22));
1341  return SVN_NO_ERROR;
1342}
1343
1344static svn_error_t *
1345bump_to_23(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1346{
1347  const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath;
1348  svn_sqlite__stmt_t *stmt;
1349  svn_boolean_t have_row;
1350
1351  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1352                                    STMT_UPGRADE_23_HAS_WORKING_NODES));
1353  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1354  SVN_ERR(svn_sqlite__reset(stmt));
1355  if (have_row)
1356    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1357                             _("The working copy at '%s' is format 22 with "
1358                               "WORKING nodes; use a format 22 client to "
1359                               "diff/revert before using this client"),
1360                             wcroot_abspath);
1361
1362  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_23));
1363  return SVN_NO_ERROR;
1364}
1365
1366static svn_error_t *
1367bump_to_24(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1368{
1369  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_24));
1370  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_NODES_TRIGGERS));
1371  return SVN_NO_ERROR;
1372}
1373
1374static svn_error_t *
1375bump_to_25(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1376{
1377  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_25));
1378  return SVN_NO_ERROR;
1379}
1380
1381static svn_error_t *
1382bump_to_26(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1383{
1384  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_26));
1385  return SVN_NO_ERROR;
1386}
1387
1388static svn_error_t *
1389bump_to_27(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1390{
1391  const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath;
1392  svn_sqlite__stmt_t *stmt;
1393  svn_boolean_t have_row;
1394
1395  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1396                                  STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS));
1397  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1398  SVN_ERR(svn_sqlite__reset(stmt));
1399  if (have_row)
1400    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1401                             _("The working copy at '%s' is format 26 with "
1402                               "conflicts; use a format 26 client to resolve "
1403                               "before using this client"),
1404                             wcroot_abspath);
1405  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_27));
1406  return SVN_NO_ERROR;
1407}
1408
1409static svn_error_t *
1410bump_to_28(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1411{
1412  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_28));
1413  return SVN_NO_ERROR;
1414}
1415
1416/* If FINFO indicates that ABSPATH names a file, rename it to
1417 * '<ABSPATH>.svn-base'.
1418 *
1419 * Ignore any file whose name is not the expected length, in order to make
1420 * life easier for any developer who runs this code twice or has some
1421 * non-standard files in the pristine directory.
1422 *
1423 * A callback for bump_to_29(), implementing #svn_io_walk_func_t. */
1424static svn_error_t *
1425rename_pristine_file(void *baton,
1426                     const char *abspath,
1427                     const apr_finfo_t *finfo,
1428                     apr_pool_t *pool)
1429{
1430  if (finfo->filetype == APR_REG
1431      && (strlen(svn_dirent_basename(abspath, pool))
1432          == PRISTINE_BASENAME_OLD_LEN))
1433    {
1434      const char *new_abspath
1435        = apr_pstrcat(pool, abspath, PRISTINE_STORAGE_EXT, SVN_VA_NULL);
1436
1437      SVN_ERR(svn_io_file_rename(abspath, new_abspath, pool));
1438    }
1439  return SVN_NO_ERROR;
1440}
1441
1442static svn_error_t *
1443upgrade_externals(struct bump_baton *bb,
1444                  svn_sqlite__db_t *sdb,
1445                  apr_pool_t *scratch_pool)
1446{
1447  svn_sqlite__stmt_t *stmt;
1448  svn_sqlite__stmt_t *stmt_add;
1449  svn_boolean_t have_row;
1450  apr_pool_t *iterpool;
1451
1452  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1453                                    STMT_SELECT_EXTERNAL_PROPERTIES));
1454
1455  SVN_ERR(svn_sqlite__get_statement(&stmt_add, sdb,
1456                                    STMT_INSERT_EXTERNAL));
1457
1458  /* ### For this intermediate upgrade we just assume WC_ID = 1.
1459     ### Before this bump we lost track of externals all the time,
1460     ### so lets keep this easy. */
1461  SVN_ERR(svn_sqlite__bindf(stmt, "is", (apr_int64_t)1, ""));
1462
1463  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1464
1465  iterpool = svn_pool_create(scratch_pool);
1466  while (have_row)
1467    {
1468      apr_hash_t *props;
1469      const char *externals;
1470
1471      svn_pool_clear(iterpool);
1472
1473      SVN_ERR(svn_sqlite__column_properties(&props, stmt, 0,
1474                                            iterpool, iterpool));
1475
1476      externals = svn_prop_get_value(props, SVN_PROP_EXTERNALS);
1477
1478      if (externals)
1479        {
1480          apr_array_header_t *ext;
1481          const char *local_relpath;
1482          const char *local_abspath;
1483          int i;
1484
1485          local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
1486          local_abspath = svn_dirent_join(bb->wcroot_abspath, local_relpath,
1487                                          iterpool);
1488
1489          SVN_ERR(svn_wc_parse_externals_description3(&ext, local_abspath,
1490                                                      externals, FALSE,
1491                                                      iterpool));
1492
1493          for (i = 0; i < ext->nelts; i++)
1494            {
1495              const svn_wc_external_item2_t *item;
1496              const char *item_relpath;
1497
1498              item = APR_ARRAY_IDX(ext, i, const svn_wc_external_item2_t *);
1499              item_relpath = svn_relpath_join(local_relpath, item->target_dir,
1500                                              iterpool);
1501
1502              /* Insert dummy externals definitions: Insert an unknown
1503                 external, to make sure it will be cleaned up when it is not
1504                 updated on the next update. */
1505              SVN_ERR(svn_sqlite__bindf(stmt_add, "isssssis",
1506                                        (apr_int64_t)1, /* wc_id */
1507                                        item_relpath,
1508                                        svn_relpath_dirname(item_relpath,
1509                                                            iterpool),
1510                                        "normal",
1511                                        "unknown",
1512                                        local_relpath,
1513                                        (apr_int64_t)1, /* repos_id */
1514                                        "" /* repos_relpath */));
1515              SVN_ERR(svn_sqlite__insert(NULL, stmt_add));
1516            }
1517        }
1518
1519      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1520    }
1521
1522  svn_pool_destroy(iterpool);
1523  return svn_error_trace(svn_sqlite__reset(stmt));
1524}
1525
1526static svn_error_t *
1527bump_to_29(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1528{
1529  struct bump_baton *bb = baton;
1530  const char *wcroot_abspath = bb->wcroot_abspath;
1531  const char *pristine_dir_abspath;
1532
1533  /* Rename all pristine files, adding a ".svn-base" suffix. */
1534  pristine_dir_abspath = svn_dirent_join_many(scratch_pool, wcroot_abspath,
1535                                              svn_wc_get_adm_dir(scratch_pool),
1536                                              PRISTINE_STORAGE_RELPATH,
1537                                              SVN_VA_NULL);
1538  SVN_ERR(svn_io_dir_walk2(pristine_dir_abspath, APR_FINFO_MIN,
1539                           rename_pristine_file, NULL, scratch_pool));
1540
1541  /* Externals */
1542  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_EXTERNALS));
1543
1544  SVN_ERR(upgrade_externals(bb, sdb, scratch_pool));
1545  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_29));
1546  return SVN_NO_ERROR;
1547}
1548
1549svn_error_t *
1550svn_wc__upgrade_conflict_skel_from_raw(svn_skel_t **conflicts,
1551                                       svn_wc__db_t *db,
1552                                       const char *wri_abspath,
1553                                       const char *local_relpath,
1554                                       const char *conflict_old,
1555                                       const char *conflict_wrk,
1556                                       const char *conflict_new,
1557                                       const char *prej_file,
1558                                       const char *tree_conflict_data,
1559                                       apr_size_t tree_conflict_len,
1560                                       apr_pool_t *result_pool,
1561                                       apr_pool_t *scratch_pool)
1562{
1563  svn_skel_t *conflict_data = NULL;
1564  const char *wcroot_abspath;
1565
1566  SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
1567                                scratch_pool, scratch_pool));
1568
1569  if (conflict_old || conflict_new || conflict_wrk)
1570    {
1571      const char *old_abspath = NULL;
1572      const char *new_abspath = NULL;
1573      const char *wrk_abspath = NULL;
1574
1575      conflict_data = svn_wc__conflict_skel_create(result_pool);
1576
1577      if (conflict_old)
1578        old_abspath = svn_dirent_join(wcroot_abspath, conflict_old,
1579                                      scratch_pool);
1580
1581      if (conflict_new)
1582        new_abspath = svn_dirent_join(wcroot_abspath, conflict_new,
1583                                      scratch_pool);
1584
1585      if (conflict_wrk)
1586        wrk_abspath = svn_dirent_join(wcroot_abspath, conflict_wrk,
1587                                      scratch_pool);
1588
1589      SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflict_data,
1590                                                      db, wri_abspath,
1591                                                      wrk_abspath,
1592                                                      old_abspath,
1593                                                      new_abspath,
1594                                                      scratch_pool,
1595                                                      scratch_pool));
1596    }
1597
1598  if (prej_file)
1599    {
1600      const char *prej_abspath;
1601
1602      if (!conflict_data)
1603        conflict_data = svn_wc__conflict_skel_create(result_pool);
1604
1605      prej_abspath = svn_dirent_join(wcroot_abspath, prej_file, scratch_pool);
1606
1607      SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflict_data,
1608                                                      db, wri_abspath,
1609                                                      prej_abspath,
1610                                                      NULL, NULL, NULL,
1611                                                apr_hash_make(scratch_pool),
1612                                                      scratch_pool,
1613                                                      scratch_pool));
1614    }
1615
1616  if (tree_conflict_data)
1617    {
1618      svn_skel_t *tc_skel;
1619      const svn_wc_conflict_description2_t *tc;
1620      const char *local_abspath;
1621
1622      if (!conflict_data)
1623        conflict_data = svn_wc__conflict_skel_create(scratch_pool);
1624
1625      tc_skel = svn_skel__parse(tree_conflict_data, tree_conflict_len,
1626                                scratch_pool);
1627
1628      local_abspath = svn_dirent_join(wcroot_abspath, local_relpath,
1629                                      scratch_pool);
1630
1631      SVN_ERR(svn_wc__deserialize_conflict(&tc, tc_skel,
1632                                           svn_dirent_dirname(local_abspath,
1633                                                              scratch_pool),
1634                                           scratch_pool, scratch_pool));
1635
1636      SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(conflict_data,
1637                                                      db, wri_abspath,
1638                                                      tc->reason,
1639                                                      tc->action,
1640                                                      NULL,
1641                                                      scratch_pool,
1642                                                      scratch_pool));
1643
1644      switch (tc->operation)
1645        {
1646          case svn_wc_operation_update:
1647          default:
1648            SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data,
1649                                                       tc->src_left_version,
1650                                                       tc->src_right_version,
1651                                                       scratch_pool,
1652                                                       scratch_pool));
1653            break;
1654          case svn_wc_operation_switch:
1655            SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict_data,
1656                                                        tc->src_left_version,
1657                                                        tc->src_right_version,
1658                                                        scratch_pool,
1659                                                        scratch_pool));
1660            break;
1661          case svn_wc_operation_merge:
1662            SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_data,
1663                                                       tc->src_left_version,
1664                                                       tc->src_right_version,
1665                                                       scratch_pool,
1666                                                       scratch_pool));
1667            break;
1668        }
1669    }
1670  else if (conflict_data)
1671    {
1672      SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data, NULL, NULL,
1673                                                  scratch_pool,
1674                                                  scratch_pool));
1675    }
1676
1677  *conflicts = conflict_data;
1678  return SVN_NO_ERROR;
1679}
1680
1681/* Helper function to upgrade a single conflict from bump_to_30 */
1682static svn_error_t *
1683bump_30_upgrade_one_conflict(svn_wc__db_t *wc_db,
1684                             const char *wcroot_abspath,
1685                             svn_sqlite__stmt_t *stmt,
1686                             svn_sqlite__db_t *sdb,
1687                             apr_pool_t *scratch_pool)
1688{
1689  svn_sqlite__stmt_t *stmt_store;
1690  svn_stringbuf_t *skel_data;
1691  svn_skel_t *conflict_data;
1692  apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0);
1693  const char *local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
1694  const char *conflict_old = svn_sqlite__column_text(stmt, 2, NULL);
1695  const char *conflict_wrk = svn_sqlite__column_text(stmt, 3, NULL);
1696  const char *conflict_new = svn_sqlite__column_text(stmt, 4, NULL);
1697  const char *prop_reject = svn_sqlite__column_text(stmt, 5, NULL);
1698  apr_size_t tree_conflict_size;
1699  const char *tree_conflict_data = svn_sqlite__column_blob(stmt, 6,
1700                                           &tree_conflict_size, NULL);
1701
1702  SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(&conflict_data,
1703                                                 wc_db, wcroot_abspath,
1704                                                 local_relpath,
1705                                                 conflict_old,
1706                                                 conflict_wrk,
1707                                                 conflict_new,
1708                                                 prop_reject,
1709                                                 tree_conflict_data,
1710                                                 tree_conflict_size,
1711                                                 scratch_pool, scratch_pool));
1712
1713  SVN_ERR_ASSERT(conflict_data != NULL);
1714
1715  skel_data = svn_skel__unparse(conflict_data, scratch_pool);
1716
1717  SVN_ERR(svn_sqlite__get_statement(&stmt_store, sdb,
1718                                    STMT_UPGRADE_30_SET_CONFLICT));
1719  SVN_ERR(svn_sqlite__bindf(stmt_store, "isb", wc_id, local_relpath,
1720                            skel_data->data, skel_data->len));
1721  SVN_ERR(svn_sqlite__step_done(stmt_store));
1722
1723  return SVN_NO_ERROR;
1724}
1725
1726static svn_error_t *
1727bump_to_30(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1728{
1729  struct bump_baton *bb = baton;
1730  svn_boolean_t have_row;
1731  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1732  svn_sqlite__stmt_t *stmt;
1733  svn_wc__db_t *db; /* Read only temp db */
1734
1735  SVN_ERR(svn_wc__db_open(&db, NULL, TRUE /* open_without_upgrade */, FALSE,
1736                          scratch_pool, scratch_pool));
1737
1738  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1739                                    STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE));
1740  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1741
1742  while (have_row)
1743    {
1744      svn_error_t *err;
1745      svn_pool_clear(iterpool);
1746
1747      err = bump_30_upgrade_one_conflict(db, bb->wcroot_abspath, stmt, sdb,
1748                                         iterpool);
1749
1750      if (err)
1751        {
1752          return svn_error_trace(
1753                    svn_error_compose_create(
1754                            err,
1755                            svn_sqlite__reset(stmt)));
1756        }
1757
1758      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1759    }
1760  SVN_ERR(svn_sqlite__reset(stmt));
1761
1762  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_30));
1763  SVN_ERR(svn_wc__db_close(db));
1764  return SVN_NO_ERROR;
1765}
1766
1767static svn_error_t *
1768bump_to_31(void *baton,
1769           svn_sqlite__db_t *sdb,
1770           apr_pool_t *scratch_pool)
1771{
1772  svn_sqlite__stmt_t *stmt, *stmt_mark_switch_roots;
1773  svn_boolean_t have_row;
1774  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1775  apr_array_header_t *empty_iprops = apr_array_make(
1776    scratch_pool, 0, sizeof(svn_prop_inherited_item_t *));
1777  svn_boolean_t iprops_column_exists = FALSE;
1778  svn_error_t *err;
1779
1780  /* Add the inherited_props column to NODES if it does not yet exist.
1781   *
1782   * When using a format >= 31 client to upgrade from old formats which
1783   * did not yet have a NODES table, the inherited_props column has
1784   * already been created as part of the NODES table. Attemping to add
1785   * the inherited_props column will raise an error in this case, so check
1786   * if the column exists first.
1787   *
1788   * Checking for the existence of a column before ALTER TABLE is not
1789   * possible within SQLite. We need to run a separate query and evaluate
1790   * its result in C first.
1791   */
1792  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_PRAGMA_TABLE_INFO_NODES));
1793  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1794  while (have_row)
1795    {
1796      const char *column_name = svn_sqlite__column_text(stmt, 1, NULL);
1797
1798      if (strcmp(column_name, "inherited_props") == 0)
1799        {
1800          iprops_column_exists = TRUE;
1801          break;
1802        }
1803
1804      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1805    }
1806  SVN_ERR(svn_sqlite__reset(stmt));
1807  if (!iprops_column_exists)
1808    SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_31_ALTER_TABLE));
1809
1810  /* Run additional statements to finalize the upgrade to format 31. */
1811  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_31_FINALIZE));
1812
1813  /* Set inherited_props to an empty array for the roots of all
1814     switched subtrees in the WC.  This allows subsequent updates
1815     to recognize these roots as needing an iprops cache. */
1816  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1817                                    STMT_UPGRADE_31_SELECT_WCROOT_NODES));
1818  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1819
1820  err = svn_sqlite__get_statement(&stmt_mark_switch_roots, sdb,
1821                                  STMT_UPDATE_IPROP);
1822  if (err)
1823    return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1824
1825  while (have_row)
1826    {
1827      const char *switched_relpath = svn_sqlite__column_text(stmt, 1, NULL);
1828      apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0);
1829
1830      err = svn_sqlite__bindf(stmt_mark_switch_roots, "is", wc_id,
1831                              switched_relpath);
1832      if (!err)
1833        err = svn_sqlite__bind_iprops(stmt_mark_switch_roots, 3,
1834                                      empty_iprops, iterpool);
1835      if (!err)
1836        err = svn_sqlite__step_done(stmt_mark_switch_roots);
1837      if (!err)
1838        err = svn_sqlite__step(&have_row, stmt);
1839
1840      if (err)
1841        return svn_error_compose_create(
1842                err,
1843                svn_error_compose_create(
1844                  /* Reset in either order is OK. */
1845                  svn_sqlite__reset(stmt),
1846                  svn_sqlite__reset(stmt_mark_switch_roots)));
1847    }
1848
1849  err = svn_sqlite__reset(stmt_mark_switch_roots);
1850  if (err)
1851    return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1852  SVN_ERR(svn_sqlite__reset(stmt));
1853
1854  svn_pool_destroy(iterpool);
1855
1856  return SVN_NO_ERROR;
1857}
1858
1859static svn_error_t *
1860upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
1861                        const char *dir_relpath,
1862                        apr_int64_t wc_id,
1863                        apr_hash_t *cache_values,
1864                        apr_pool_t *scratch_pool)
1865{
1866  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1867  apr_hash_index_t *hi;
1868  svn_sqlite__stmt_t *stmt;
1869
1870  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1871                                    STMT_UPDATE_BASE_NODE_DAV_CACHE));
1872
1873  /* Iterate over all the wcprops, writing each one to the wc_db. */
1874  for (hi = apr_hash_first(scratch_pool, cache_values);
1875       hi;
1876       hi = apr_hash_next(hi))
1877    {
1878      const char *name = apr_hash_this_key(hi);
1879      apr_hash_t *props = apr_hash_this_val(hi);
1880      const char *local_relpath;
1881
1882      svn_pool_clear(iterpool);
1883
1884      local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
1885
1886      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
1887      SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
1888      SVN_ERR(svn_sqlite__step_done(stmt));
1889    }
1890
1891  svn_pool_destroy(iterpool);
1892
1893  return SVN_NO_ERROR;
1894}
1895
1896
1897struct upgrade_data_t {
1898  svn_sqlite__db_t *sdb;
1899  const char *root_abspath;
1900  apr_int64_t repos_id;
1901  apr_int64_t wc_id;
1902};
1903
1904/* Upgrade the working copy directory represented by DB/DIR_ABSPATH
1905   from OLD_FORMAT to the wc-ng format (SVN_WC__WC_NG_VERSION)'.
1906
1907   Pass REPOS_INFO_FUNC, REPOS_INFO_BATON and REPOS_CACHE to
1908   ensure_repos_info. Add the found repository root and UUID to
1909   REPOS_CACHE if it doesn't have a cached entry for this
1910   repository.
1911
1912   *DATA refers to the single root db.
1913
1914   Uses SCRATCH_POOL for all temporary allocation.  */
1915static svn_error_t *
1916upgrade_to_wcng(void **dir_baton,
1917                void *parent_baton,
1918                svn_wc__db_t *db,
1919                const char *dir_abspath,
1920                int old_format,
1921                apr_int64_t wc_id,
1922                svn_wc_upgrade_get_repos_info_t repos_info_func,
1923                void *repos_info_baton,
1924                apr_hash_t *repos_cache,
1925                const struct upgrade_data_t *data,
1926                apr_pool_t *result_pool,
1927                apr_pool_t *scratch_pool)
1928{
1929  const char *logfile_path = svn_wc__adm_child(dir_abspath, ADM_LOG,
1930                                               scratch_pool);
1931  svn_node_kind_t logfile_on_disk_kind;
1932  apr_hash_t *entries;
1933  svn_wc_entry_t *this_dir;
1934  const char *old_wcroot_abspath, *dir_relpath;
1935  apr_hash_t *text_bases_info;
1936  svn_error_t *err;
1937
1938  /* Don't try to mess with the WC if there are old log files left. */
1939
1940  /* Is the (first) log file present?  */
1941  SVN_ERR(svn_io_check_path(logfile_path, &logfile_on_disk_kind,
1942                            scratch_pool));
1943  if (logfile_on_disk_kind == svn_node_file)
1944    return svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
1945                            _("Cannot upgrade with existing logs; run a "
1946                              "cleanup operation on this working copy using "
1947                              "a client version which is compatible with this "
1948                              "working copy's format (such as the version "
1949                              "you are upgrading from), then retry the "
1950                              "upgrade with the current version"));
1951
1952  /* Lock this working copy directory, or steal an existing lock. Do this
1953     BEFORE we read the entries. We don't want another process to modify the
1954     entries after we've read them into memory.  */
1955  SVN_ERR(create_physical_lock(dir_abspath, scratch_pool));
1956
1957  /* What's going on here?
1958   *
1959   * We're attempting to upgrade an older working copy to the new wc-ng format.
1960   * The semantics and storage mechanisms between the two are vastly different,
1961   * so it's going to be a bit painful.  Here's a plan for the operation:
1962   *
1963   * 1) Read the old 'entries' using the old-format reader.
1964   *
1965   * 2) Create the new DB if it hasn't already been created.
1966   *
1967   * 3) Use our compatibility code for writing entries to fill out the (new)
1968   *    DB state.  Use the remembered checksums, since an entry has only the
1969   *    MD5 not the SHA1 checksum, and in the case of a revert-base doesn't
1970   *    even have that.
1971   *
1972   * 4) Convert wcprop to the wc-ng format
1973   *
1974   * 5) Migrate regular properties to the WC-NG DB.
1975   */
1976
1977  /***** ENTRIES - READ *****/
1978  SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath,
1979                                   scratch_pool, scratch_pool));
1980
1981  this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
1982  SVN_ERR(ensure_repos_info(this_dir, dir_abspath,
1983                            repos_info_func, repos_info_baton,
1984                            repos_cache,
1985                            scratch_pool, scratch_pool));
1986
1987  /* Cache repos UUID pairs for when a subdir doesn't have this information */
1988  if (!svn_hash_gets(repos_cache, this_dir->repos))
1989    {
1990      apr_pool_t *hash_pool = apr_hash_pool_get(repos_cache);
1991
1992      svn_hash_sets(repos_cache,
1993                    apr_pstrdup(hash_pool, this_dir->repos),
1994                    apr_pstrdup(hash_pool, this_dir->uuid));
1995    }
1996
1997  old_wcroot_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
1998                                                       data->root_abspath,
1999                                                       scratch_pool);
2000  dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath, dir_abspath);
2001
2002  /***** TEXT BASES *****/
2003  SVN_ERR(migrate_text_bases(&text_bases_info, dir_abspath, data->root_abspath,
2004                             data->sdb, scratch_pool, scratch_pool));
2005
2006  /***** ENTRIES - WRITE *****/
2007  err = svn_wc__write_upgraded_entries(dir_baton, parent_baton, db, data->sdb,
2008                                       data->repos_id, data->wc_id,
2009                                       dir_abspath, data->root_abspath,
2010                                       entries, text_bases_info,
2011                                       result_pool, scratch_pool);
2012  if (err && err->apr_err == SVN_ERR_WC_CORRUPT)
2013    return svn_error_quick_wrap(err,
2014                                _("This working copy is corrupt and "
2015                                  "cannot be upgraded. Please check out "
2016                                  "a new working copy."));
2017  else
2018    SVN_ERR(err);
2019
2020  /***** WC PROPS *****/
2021  /* If we don't know precisely where the wcprops are, ignore them.  */
2022  if (old_format != SVN_WC__WCPROPS_LOST)
2023    {
2024      apr_hash_t *all_wcprops;
2025
2026      if (old_format <= SVN_WC__WCPROPS_MANY_FILES_VERSION)
2027        SVN_ERR(read_many_wcprops(&all_wcprops, dir_abspath,
2028                                  scratch_pool, scratch_pool));
2029      else
2030        SVN_ERR(read_wcprops(&all_wcprops, dir_abspath,
2031                             scratch_pool, scratch_pool));
2032
2033      SVN_ERR(upgrade_apply_dav_cache(data->sdb, dir_relpath, wc_id,
2034                                      all_wcprops, scratch_pool));
2035    }
2036
2037  /* Upgrade all the properties (including "this dir").
2038
2039     Note: this must come AFTER the entries have been migrated into the
2040     database. The upgrade process needs the children in BASE_NODE and
2041     WORKING_NODE, and to examine the resultant WORKING state.  */
2042  SVN_ERR(migrate_props(dir_abspath, data->root_abspath, data->sdb, old_format,
2043                        wc_id, scratch_pool));
2044
2045  return SVN_NO_ERROR;
2046}
2047
2048const char *
2049svn_wc__version_string_from_format(int wc_format)
2050{
2051  switch (wc_format)
2052    {
2053      case 4: return "<=1.3";
2054      case 8: return "1.4";
2055      case 9: return "1.5";
2056      case 10: return "1.6";
2057      case SVN_WC__WC_NG_VERSION: return "1.7";
2058    }
2059  return _("(unreleased development version)");
2060}
2061
2062svn_error_t *
2063svn_wc__upgrade_sdb(int *result_format,
2064                    const char *wcroot_abspath,
2065                    svn_sqlite__db_t *sdb,
2066                    int start_format,
2067                    apr_pool_t *scratch_pool)
2068{
2069  struct bump_baton bb;
2070
2071  bb.wcroot_abspath = wcroot_abspath;
2072
2073  if (start_format < SVN_WC__WC_NG_VERSION /* 12 */)
2074    return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
2075                             _("Working copy '%s' is too old (format %d, "
2076                               "created by Subversion %s)"),
2077                             svn_dirent_local_style(wcroot_abspath,
2078                                                    scratch_pool),
2079                             start_format,
2080                             svn_wc__version_string_from_format(start_format));
2081
2082  /* Early WCNG formats no longer supported. */
2083  if (start_format < 19)
2084    return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
2085                             _("Working copy '%s' is an old development "
2086                               "version (format %d); to upgrade it, "
2087                               "use a format 18 client, then "
2088                               "use 'tools/dev/wc-ng/bump-to-19.py', then "
2089                               "use the current client"),
2090                             svn_dirent_local_style(wcroot_abspath,
2091                                                    scratch_pool),
2092                             start_format);
2093
2094  /* ### need lock-out. only one upgrade at a time. note that other code
2095     ### cannot use this un-upgraded database until we finish the upgrade.  */
2096
2097  /* Note: none of these have "break" statements; the fall-through is
2098     intentional. */
2099  switch (start_format)
2100    {
2101      case 19:
2102        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_20, &bb,
2103                                             scratch_pool));
2104        *result_format = 20;
2105        /* FALLTHROUGH  */
2106
2107      case 20:
2108        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_21, &bb,
2109                                             scratch_pool));
2110        *result_format = 21;
2111        /* FALLTHROUGH  */
2112
2113      case 21:
2114        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_22, &bb,
2115                                             scratch_pool));
2116        *result_format = 22;
2117        /* FALLTHROUGH  */
2118
2119      case 22:
2120        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_23, &bb,
2121                                             scratch_pool));
2122        *result_format = 23;
2123        /* FALLTHROUGH  */
2124
2125      case 23:
2126        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_24, &bb,
2127                                             scratch_pool));
2128        *result_format = 24;
2129        /* FALLTHROUGH  */
2130
2131      case 24:
2132        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_25, &bb,
2133                                             scratch_pool));
2134        *result_format = 25;
2135        /* FALLTHROUGH  */
2136
2137      case 25:
2138        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_26, &bb,
2139                                             scratch_pool));
2140        *result_format = 26;
2141        /* FALLTHROUGH  */
2142
2143      case 26:
2144        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_27, &bb,
2145                                             scratch_pool));
2146        *result_format = 27;
2147        /* FALLTHROUGH  */
2148
2149      case 27:
2150        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_28, &bb,
2151                                             scratch_pool));
2152        *result_format = 28;
2153        /* FALLTHROUGH  */
2154
2155      case 28:
2156        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_29, &bb,
2157                                             scratch_pool));
2158        *result_format = 29;
2159        /* FALLTHROUGH  */
2160
2161      case 29:
2162        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_30, &bb,
2163                                             scratch_pool));
2164        *result_format = 30;
2165
2166      case 30:
2167        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_31, &bb,
2168                                             scratch_pool));
2169        *result_format = 31;
2170        /* FALLTHROUGH  */
2171      /* ### future bumps go here.  */
2172#if 0
2173      case XXX-1:
2174        /* Revamp the recording of tree conflicts.  */
2175        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_XXX, &bb,
2176                                             scratch_pool));
2177        *result_format = XXX;
2178        /* FALLTHROUGH  */
2179#endif
2180      case SVN_WC__VERSION:
2181        /* already upgraded */
2182        *result_format = SVN_WC__VERSION;
2183
2184        SVN_SQLITE__WITH_LOCK(
2185            svn_wc__db_install_schema_statistics(sdb, scratch_pool),
2186            sdb);
2187    }
2188
2189#ifdef SVN_DEBUG
2190  if (*result_format != start_format)
2191    {
2192      int schema_version;
2193      SVN_ERR(svn_sqlite__read_schema_version(&schema_version, sdb, scratch_pool));
2194
2195      /* If this assertion fails the schema isn't updated correctly */
2196      SVN_ERR_ASSERT(schema_version == *result_format);
2197    }
2198#endif
2199
2200  /* Zap anything that might be remaining or escaped our notice.  */
2201  wipe_obsolete_files(wcroot_abspath, scratch_pool);
2202
2203  return SVN_NO_ERROR;
2204}
2205
2206
2207/* */
2208static svn_error_t *
2209upgrade_working_copy(void *parent_baton,
2210                     svn_wc__db_t *db,
2211                     const char *dir_abspath,
2212                     svn_wc_upgrade_get_repos_info_t repos_info_func,
2213                     void *repos_info_baton,
2214                     apr_hash_t *repos_cache,
2215                     const struct upgrade_data_t *data,
2216                     svn_cancel_func_t cancel_func,
2217                     void *cancel_baton,
2218                     svn_wc_notify_func2_t notify_func,
2219                     void *notify_baton,
2220                     apr_pool_t *result_pool,
2221                     apr_pool_t *scratch_pool)
2222{
2223  void *dir_baton;
2224  int old_format;
2225  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2226  apr_array_header_t *subdirs;
2227  svn_error_t *err;
2228  int i;
2229
2230  if (cancel_func)
2231    SVN_ERR(cancel_func(cancel_baton));
2232
2233  SVN_ERR(svn_wc__db_temp_get_format(&old_format, db, dir_abspath,
2234                                     iterpool));
2235
2236  if (old_format >= SVN_WC__WC_NG_VERSION)
2237    {
2238      if (notify_func)
2239        notify_func(notify_baton,
2240                    svn_wc_create_notify(dir_abspath, svn_wc_notify_skip,
2241                                         iterpool),
2242                iterpool);
2243      svn_pool_destroy(iterpool);
2244      return SVN_NO_ERROR;
2245    }
2246
2247  err = get_versioned_subdirs(&subdirs, NULL, dir_abspath, FALSE,
2248                              scratch_pool, iterpool);
2249  if (err)
2250    {
2251      if (APR_STATUS_IS_ENOENT(err->apr_err)
2252          || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))
2253        {
2254          /* An unversioned dir is obstructing a versioned dir */
2255          svn_error_clear(err);
2256          err = NULL;
2257          if (notify_func)
2258            notify_func(notify_baton,
2259                        svn_wc_create_notify(dir_abspath, svn_wc_notify_skip,
2260                                             iterpool),
2261                        iterpool);
2262        }
2263      svn_pool_destroy(iterpool);
2264      return err;
2265    }
2266
2267
2268  SVN_ERR(upgrade_to_wcng(&dir_baton, parent_baton, db, dir_abspath,
2269                          old_format, data->wc_id,
2270                          repos_info_func, repos_info_baton,
2271                          repos_cache, data, scratch_pool, iterpool));
2272
2273  if (notify_func)
2274    notify_func(notify_baton,
2275                svn_wc_create_notify(dir_abspath, svn_wc_notify_upgraded_path,
2276                                     iterpool),
2277                iterpool);
2278
2279  for (i = 0; i < subdirs->nelts; ++i)
2280    {
2281      const char *child_abspath = APR_ARRAY_IDX(subdirs, i, const char *);
2282
2283      svn_pool_clear(iterpool);
2284
2285      SVN_ERR(upgrade_working_copy(dir_baton, db, child_abspath,
2286                                   repos_info_func, repos_info_baton,
2287                                   repos_cache, data,
2288                                   cancel_func, cancel_baton,
2289                                   notify_func, notify_baton,
2290                                   iterpool, iterpool));
2291    }
2292
2293  svn_pool_destroy(iterpool);
2294
2295  return SVN_NO_ERROR;
2296}
2297
2298
2299/* Return a verbose error if LOCAL_ABSPATH is a not a pre-1.7 working
2300   copy root */
2301static svn_error_t *
2302is_old_wcroot(const char *local_abspath,
2303              apr_pool_t *scratch_pool)
2304{
2305  apr_hash_t *entries;
2306  const char *parent_abspath, *name;
2307  svn_wc_entry_t *entry;
2308  svn_error_t *err = svn_wc__read_entries_old(&entries, local_abspath,
2309                                              scratch_pool, scratch_pool);
2310  if (err)
2311    {
2312      return svn_error_createf(
2313        SVN_ERR_WC_INVALID_OP_ON_CWD, err,
2314        _("Can't upgrade '%s' as it is not a working copy"),
2315        svn_dirent_local_style(local_abspath, scratch_pool));
2316    }
2317  else if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
2318    return SVN_NO_ERROR;
2319
2320  svn_dirent_split(&parent_abspath, &name, local_abspath, scratch_pool);
2321
2322  err = svn_wc__read_entries_old(&entries, parent_abspath,
2323                                 scratch_pool, scratch_pool);
2324  if (err)
2325    {
2326      svn_error_clear(err);
2327      return SVN_NO_ERROR;
2328    }
2329
2330  entry = svn_hash_gets(entries, name);
2331  if (!entry
2332      || entry->absent
2333      || (entry->deleted && entry->schedule != svn_wc_schedule_add)
2334      || entry->depth == svn_depth_exclude)
2335    {
2336      return SVN_NO_ERROR;
2337    }
2338
2339  while (!svn_dirent_is_root(parent_abspath, strlen(parent_abspath)))
2340    {
2341      svn_dirent_split(&parent_abspath, &name, parent_abspath, scratch_pool);
2342      err = svn_wc__read_entries_old(&entries, parent_abspath,
2343                                     scratch_pool, scratch_pool);
2344      if (err)
2345        {
2346          svn_error_clear(err);
2347          parent_abspath = svn_dirent_join(parent_abspath, name, scratch_pool);
2348          break;
2349        }
2350      entry = svn_hash_gets(entries, name);
2351      if (!entry
2352          || entry->absent
2353          || (entry->deleted && entry->schedule != svn_wc_schedule_add)
2354          || entry->depth == svn_depth_exclude)
2355        {
2356          parent_abspath = svn_dirent_join(parent_abspath, name, scratch_pool);
2357          break;
2358        }
2359    }
2360
2361  return svn_error_createf(
2362    SVN_ERR_WC_INVALID_OP_ON_CWD, NULL,
2363    _("Can't upgrade '%s' as it is not a working copy root,"
2364      " the root is '%s'"),
2365    svn_dirent_local_style(local_abspath, scratch_pool),
2366    svn_dirent_local_style(parent_abspath, scratch_pool));
2367}
2368
2369svn_error_t *
2370svn_wc_upgrade(svn_wc_context_t *wc_ctx,
2371               const char *local_abspath,
2372               svn_wc_upgrade_get_repos_info_t repos_info_func,
2373               void *repos_info_baton,
2374               svn_cancel_func_t cancel_func,
2375               void *cancel_baton,
2376               svn_wc_notify_func2_t notify_func,
2377               void *notify_baton,
2378               apr_pool_t *scratch_pool)
2379{
2380  svn_wc__db_t *db;
2381  struct upgrade_data_t data = { NULL };
2382  svn_skel_t *work_item, *work_items = NULL;
2383  const char *pristine_from, *pristine_to, *db_from, *db_to;
2384  apr_hash_t *repos_cache = apr_hash_make(scratch_pool);
2385  svn_wc_entry_t *this_dir;
2386  apr_hash_t *entries;
2387  const char *root_adm_abspath;
2388  svn_error_t *err;
2389  int result_format;
2390  svn_boolean_t bumped_format;
2391
2392  /* Try upgrading a wc-ng-style working copy. */
2393  SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, TRUE, FALSE,
2394                          scratch_pool, scratch_pool));
2395
2396
2397  err = svn_wc__db_bump_format(&result_format, &bumped_format,
2398                               db, local_abspath,
2399                               scratch_pool);
2400  if (err)
2401    {
2402      if (err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
2403        {
2404          return svn_error_trace(
2405                    svn_error_compose_create(
2406                            err,
2407                            svn_wc__db_close(db)));
2408        }
2409
2410      svn_error_clear(err);
2411      /* Pre 1.7: Fall through */
2412    }
2413  else
2414    {
2415      /* Auto-upgrade worked! */
2416      SVN_ERR(svn_wc__db_close(db));
2417
2418      SVN_ERR_ASSERT(result_format == SVN_WC__VERSION);
2419
2420      if (bumped_format && notify_func)
2421        {
2422          svn_wc_notify_t *notify;
2423
2424          notify = svn_wc_create_notify(local_abspath,
2425                                        svn_wc_notify_upgraded_path,
2426                                        scratch_pool);
2427
2428          notify_func(notify_baton, notify, scratch_pool);
2429        }
2430
2431      return SVN_NO_ERROR;
2432    }
2433
2434  SVN_ERR(is_old_wcroot(local_abspath, scratch_pool));
2435
2436  /* Given a pre-wcng root some/wc we create a temporary wcng in
2437     some/wc/.svn/tmp/wcng/wc.db and copy the metadata from one to the
2438     other, then the temporary wc.db file gets moved into the original
2439     root.  Until the wc.db file is moved the original working copy
2440     remains a pre-wcng and 'cleanup' with an old client will remove
2441     the partial upgrade.  Moving the wc.db file creates a wcng, and
2442     'cleanup' with a new client will complete any outstanding
2443     upgrade. */
2444
2445  SVN_ERR(svn_wc__read_entries_old(&entries, local_abspath,
2446                                   scratch_pool, scratch_pool));
2447
2448  this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2449  SVN_ERR(ensure_repos_info(this_dir, local_abspath, repos_info_func,
2450                            repos_info_baton, repos_cache,
2451                            scratch_pool, scratch_pool));
2452
2453  /* Cache repos UUID pairs for when a subdir doesn't have this information */
2454  if (!svn_hash_gets(repos_cache, this_dir->repos))
2455    svn_hash_sets(repos_cache,
2456                  apr_pstrdup(scratch_pool, this_dir->repos),
2457                  apr_pstrdup(scratch_pool, this_dir->uuid));
2458
2459  /* Create the new DB in the temporary root wc/.svn/tmp/wcng/.svn */
2460  data.root_abspath = svn_dirent_join(svn_wc__adm_child(local_abspath, "tmp",
2461                                                        scratch_pool),
2462                                       "wcng", scratch_pool);
2463  root_adm_abspath = svn_wc__adm_child(data.root_abspath, "",
2464                                       scratch_pool);
2465  SVN_ERR(svn_io_remove_dir2(root_adm_abspath, TRUE, NULL, NULL,
2466                             scratch_pool));
2467  SVN_ERR(svn_wc__ensure_directory(root_adm_abspath, scratch_pool));
2468
2469  /* Create an empty sqlite database for this directory and store it in DB. */
2470  SVN_ERR(svn_wc__db_upgrade_begin(&data.sdb,
2471                                   &data.repos_id, &data.wc_id,
2472                                   db, data.root_abspath,
2473                                   this_dir->repos, this_dir->uuid,
2474                                   scratch_pool));
2475
2476  /* Migrate the entries over to the new database.
2477   ### We need to think about atomicity here.
2478
2479   entries_write_new() writes in current format rather than
2480   f12. Thus, this function bumps a working copy all the way to
2481   current.  */
2482  SVN_ERR(svn_wc__db_wclock_obtain(db, data.root_abspath, 0, FALSE,
2483                                   scratch_pool));
2484
2485  SVN_SQLITE__WITH_LOCK(
2486    upgrade_working_copy(NULL, db, local_abspath,
2487                         repos_info_func, repos_info_baton,
2488                         repos_cache, &data,
2489                         cancel_func, cancel_baton,
2490                         notify_func, notify_baton,
2491                         scratch_pool, scratch_pool),
2492    data.sdb);
2493
2494  /* A workqueue item to move the pristine dir into place */
2495  pristine_from = svn_wc__adm_child(data.root_abspath, PRISTINE_STORAGE_RELPATH,
2496                                    scratch_pool);
2497  pristine_to = svn_wc__adm_child(local_abspath, PRISTINE_STORAGE_RELPATH,
2498                                  scratch_pool);
2499  SVN_ERR(svn_wc__ensure_directory(pristine_from, scratch_pool));
2500  SVN_ERR(svn_wc__wq_build_file_move(&work_item, db, local_abspath,
2501                                     pristine_from, pristine_to,
2502                                     scratch_pool, scratch_pool));
2503  work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2504
2505  /* A workqueue item to remove pre-wcng metadata */
2506  SVN_ERR(svn_wc__wq_build_postupgrade(&work_item, scratch_pool));
2507  work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2508  SVN_ERR(svn_wc__db_wq_add(db, data.root_abspath, work_items, scratch_pool));
2509
2510  SVN_ERR(svn_wc__db_wclock_release(db, data.root_abspath, scratch_pool));
2511  SVN_ERR(svn_wc__db_close(db));
2512
2513  /* Renaming the db file is what makes the pre-wcng into a wcng */
2514  db_from = svn_wc__adm_child(data.root_abspath, SDB_FILE, scratch_pool);
2515  db_to = svn_wc__adm_child(local_abspath, SDB_FILE, scratch_pool);
2516  SVN_ERR(svn_io_file_rename(db_from, db_to, scratch_pool));
2517
2518  /* Now we have a working wcng, tidy up the droppings */
2519  SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, FALSE, FALSE,
2520                          scratch_pool, scratch_pool));
2521  SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2522                         scratch_pool));
2523  SVN_ERR(svn_wc__db_close(db));
2524
2525  /* Should we have the workqueue remove this empty dir? */
2526  SVN_ERR(svn_io_remove_dir2(data.root_abspath, FALSE, NULL, NULL,
2527                             scratch_pool));
2528
2529  return SVN_NO_ERROR;
2530}
2531
2532svn_error_t *
2533svn_wc__upgrade_add_external_info(svn_wc_context_t *wc_ctx,
2534                                  const char *local_abspath,
2535                                  svn_node_kind_t kind,
2536                                  const char *def_local_abspath,
2537                                  const char *repos_relpath,
2538                                  const char *repos_root_url,
2539                                  const char *repos_uuid,
2540                                  svn_revnum_t def_peg_revision,
2541                                  svn_revnum_t def_revision,
2542                                  apr_pool_t *scratch_pool)
2543{
2544  svn_node_kind_t db_kind;
2545  switch (kind)
2546    {
2547      case svn_node_dir:
2548        db_kind = svn_node_dir;
2549        break;
2550
2551      case svn_node_file:
2552        db_kind = svn_node_file;
2553        break;
2554
2555      case svn_node_unknown:
2556        db_kind = svn_node_unknown;
2557        break;
2558
2559      default:
2560        SVN_ERR_MALFUNCTION();
2561    }
2562
2563  SVN_ERR(svn_wc__db_upgrade_insert_external(wc_ctx->db, local_abspath,
2564                                             db_kind,
2565                                             svn_dirent_dirname(local_abspath,
2566                                                                scratch_pool),
2567                                             def_local_abspath, repos_relpath,
2568                                             repos_root_url, repos_uuid,
2569                                             def_peg_revision, def_revision,
2570                                             scratch_pool));
2571  return SVN_NO_ERROR;
2572}
2573