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