1251881Speter/*
2251881Speter * upgrade.c:  routines for upgrading a working copy
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter#include <apr_pools.h>
25251881Speter
26251881Speter#include "svn_types.h"
27251881Speter#include "svn_pools.h"
28251881Speter#include "svn_dirent_uri.h"
29251881Speter#include "svn_path.h"
30251881Speter#include "svn_hash.h"
31251881Speter
32251881Speter#include "wc.h"
33251881Speter#include "adm_files.h"
34251881Speter#include "conflicts.h"
35251881Speter#include "entries.h"
36251881Speter#include "wc_db.h"
37251881Speter#include "tree_conflicts.h"
38251881Speter#include "wc-queries.h"  /* for STMT_*  */
39251881Speter#include "workqueue.h"
40289180Speter#include "token-map.h"
41251881Speter
42251881Speter#include "svn_private_config.h"
43251881Speter#include "private/svn_wc_private.h"
44251881Speter#include "private/svn_sqlite.h"
45251881Speter#include "private/svn_token.h"
46251881Speter
47251881Speter/* WC-1.0 administrative area extensions */
48251881Speter#define SVN_WC__BASE_EXT      ".svn-base" /* for text and prop bases */
49251881Speter#define SVN_WC__WORK_EXT      ".svn-work" /* for working propfiles */
50251881Speter#define SVN_WC__REVERT_EXT    ".svn-revert" /* for reverting a replaced
51251881Speter                                               file */
52251881Speter
53251881Speter/* Old locations for storing "wcprops" (aka "dav cache").  */
54251881Speter#define WCPROPS_SUBDIR_FOR_FILES "wcprops"
55251881Speter#define WCPROPS_FNAME_FOR_DIR "dir-wcprops"
56251881Speter#define WCPROPS_ALL_DATA "all-wcprops"
57251881Speter
58251881Speter/* Old property locations. */
59251881Speter#define PROPS_SUBDIR "props"
60251881Speter#define PROP_BASE_SUBDIR "prop-base"
61251881Speter#define PROP_BASE_FOR_DIR "dir-prop-base"
62251881Speter#define PROP_REVERT_FOR_DIR "dir-prop-revert"
63251881Speter#define PROP_WORKING_FOR_DIR "dir-props"
64251881Speter
65251881Speter/* Old textbase location. */
66251881Speter#define TEXT_BASE_SUBDIR "text-base"
67251881Speter
68251881Speter#define TEMP_DIR "tmp"
69251881Speter
70251881Speter/* Old data files that we no longer need/use.  */
71251881Speter#define ADM_README "README.txt"
72251881Speter#define ADM_EMPTY_FILE "empty-file"
73251881Speter#define ADM_LOG "log"
74251881Speter#define ADM_LOCK "lock"
75251881Speter
76251881Speter/* New pristine location */
77251881Speter#define PRISTINE_STORAGE_RELPATH "pristine"
78251881Speter#define PRISTINE_STORAGE_EXT ".svn-base"
79251881Speter/* Number of characters in a pristine file basename, in WC format <= 28. */
80251881Speter#define PRISTINE_BASENAME_OLD_LEN 40
81251881Speter#define SDB_FILE  "wc.db"
82251881Speter
83251881Speter
84251881Speter/* Read the properties from the file at PROPFILE_ABSPATH, returning them
85251881Speter   as a hash in *PROPS. If the propfile is NOT present, then NULL will
86251881Speter   be returned in *PROPS.  */
87251881Speterstatic svn_error_t *
88251881Speterread_propfile(apr_hash_t **props,
89251881Speter              const char *propfile_abspath,
90251881Speter              apr_pool_t *result_pool,
91251881Speter              apr_pool_t *scratch_pool)
92251881Speter{
93251881Speter  svn_error_t *err;
94251881Speter  svn_stream_t *stream;
95251881Speter  apr_finfo_t finfo;
96251881Speter
97251881Speter  err = svn_io_stat(&finfo, propfile_abspath, APR_FINFO_SIZE, scratch_pool);
98251881Speter
99251881Speter  if (err
100251881Speter      && (APR_STATUS_IS_ENOENT(err->apr_err)
101251881Speter          || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
102251881Speter    {
103251881Speter      svn_error_clear(err);
104251881Speter
105251881Speter      /* The propfile was not there. Signal with a NULL.  */
106251881Speter      *props = NULL;
107251881Speter      return SVN_NO_ERROR;
108251881Speter    }
109251881Speter  else
110251881Speter    SVN_ERR(err);
111251881Speter
112251881Speter  /* A 0-bytes file signals an empty property list.
113251881Speter     (mostly used for revert-props) */
114251881Speter  if (finfo.size == 0)
115251881Speter    {
116251881Speter      *props = apr_hash_make(result_pool);
117251881Speter      return SVN_NO_ERROR;
118251881Speter    }
119251881Speter
120251881Speter  SVN_ERR(svn_stream_open_readonly(&stream, propfile_abspath,
121251881Speter                                   scratch_pool, scratch_pool));
122251881Speter
123251881Speter  /* ### does this function need to be smarter? will we see zero-length
124251881Speter     ### files? see props.c::load_props(). there may be more work here.
125251881Speter     ### need a historic analysis of 1.x property storage. what will we
126251881Speter     ### actually run into?  */
127251881Speter
128251881Speter  /* ### loggy_write_properties() and immediate_install_props() write
129251881Speter     ### zero-length files for "no props", so we should be a bit smarter
130251881Speter     ### in here.  */
131251881Speter
132251881Speter  /* ### should we be forgiving in here? I say "no". if we can't be sure,
133251881Speter     ### then we could effectively corrupt the local working copy.  */
134251881Speter
135251881Speter  *props = apr_hash_make(result_pool);
136251881Speter  SVN_ERR(svn_hash_read2(*props, stream, SVN_HASH_TERMINATOR, result_pool));
137251881Speter
138251881Speter  return svn_error_trace(svn_stream_close(stream));
139251881Speter}
140251881Speter
141251881Speter
142251881Speter/* Read one proplist (allocated from RESULT_POOL) from STREAM, and place it
143251881Speter   into ALL_WCPROPS at NAME.  */
144251881Speterstatic svn_error_t *
145251881Speterread_one_proplist(apr_hash_t *all_wcprops,
146251881Speter                  const char *name,
147251881Speter                  svn_stream_t *stream,
148251881Speter                  apr_pool_t *result_pool,
149251881Speter                  apr_pool_t *scratch_pool)
150251881Speter{
151251881Speter  apr_hash_t *proplist;
152251881Speter
153251881Speter  proplist = apr_hash_make(result_pool);
154251881Speter  SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, result_pool));
155251881Speter  svn_hash_sets(all_wcprops, name, proplist);
156251881Speter
157251881Speter  return SVN_NO_ERROR;
158251881Speter}
159251881Speter
160251881Speter
161251881Speter/* Read the wcprops from all the files in the admin area of DIR_ABSPATH,
162251881Speter   returning them in *ALL_WCPROPS. Results are allocated in RESULT_POOL,
163251881Speter   and temporary allocations are performed in SCRATCH_POOL.  */
164251881Speterstatic svn_error_t *
165251881Speterread_many_wcprops(apr_hash_t **all_wcprops,
166251881Speter                  const char *dir_abspath,
167251881Speter                  apr_pool_t *result_pool,
168251881Speter                  apr_pool_t *scratch_pool)
169251881Speter{
170251881Speter  const char *propfile_abspath;
171251881Speter  apr_hash_t *wcprops;
172251881Speter  apr_hash_t *dirents;
173251881Speter  const char *props_dir_abspath;
174251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
175251881Speter  apr_hash_index_t *hi;
176251881Speter
177251881Speter  *all_wcprops = apr_hash_make(result_pool);
178251881Speter
179251881Speter  /* First, look at dir-wcprops. */
180251881Speter  propfile_abspath = svn_wc__adm_child(dir_abspath, WCPROPS_FNAME_FOR_DIR,
181251881Speter                                       scratch_pool);
182251881Speter  SVN_ERR(read_propfile(&wcprops, propfile_abspath, result_pool, iterpool));
183251881Speter  if (wcprops != NULL)
184251881Speter    svn_hash_sets(*all_wcprops, SVN_WC_ENTRY_THIS_DIR, wcprops);
185251881Speter
186251881Speter  props_dir_abspath = svn_wc__adm_child(dir_abspath, WCPROPS_SUBDIR_FOR_FILES,
187251881Speter                                        scratch_pool);
188251881Speter
189251881Speter  /* Now walk the wcprops directory. */
190251881Speter  SVN_ERR(svn_io_get_dirents3(&dirents, props_dir_abspath, TRUE,
191251881Speter                              scratch_pool, scratch_pool));
192251881Speter
193251881Speter  for (hi = apr_hash_first(scratch_pool, dirents);
194251881Speter       hi;
195251881Speter       hi = apr_hash_next(hi))
196251881Speter    {
197289180Speter      const char *name = apr_hash_this_key(hi);
198251881Speter
199251881Speter      svn_pool_clear(iterpool);
200251881Speter
201251881Speter      propfile_abspath = svn_dirent_join(props_dir_abspath, name, iterpool);
202251881Speter
203251881Speter      SVN_ERR(read_propfile(&wcprops, propfile_abspath,
204251881Speter                            result_pool, iterpool));
205251881Speter      SVN_ERR_ASSERT(wcprops != NULL);
206251881Speter      svn_hash_sets(*all_wcprops, apr_pstrdup(result_pool, name), wcprops);
207251881Speter    }
208251881Speter
209251881Speter  svn_pool_destroy(iterpool);
210251881Speter  return SVN_NO_ERROR;
211251881Speter}
212251881Speter
213251881Speter
214251881Speter/* For wcprops stored in a single file in this working copy, read that
215251881Speter   file and return it in *ALL_WCPROPS, allocated in RESULT_POOL.   Use
216251881Speter   SCRATCH_POOL for temporary allocations. */
217251881Speterstatic svn_error_t *
218251881Speterread_wcprops(apr_hash_t **all_wcprops,
219251881Speter             const char *dir_abspath,
220251881Speter             apr_pool_t *result_pool,
221251881Speter             apr_pool_t *scratch_pool)
222251881Speter{
223251881Speter  svn_stream_t *stream;
224251881Speter  svn_error_t *err;
225251881Speter
226251881Speter  *all_wcprops = apr_hash_make(result_pool);
227251881Speter
228251881Speter  err = svn_wc__open_adm_stream(&stream, dir_abspath,
229251881Speter                                WCPROPS_ALL_DATA,
230251881Speter                                scratch_pool, scratch_pool);
231251881Speter
232251881Speter  /* A non-existent file means there are no props. */
233251881Speter  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
234251881Speter    {
235251881Speter      svn_error_clear(err);
236251881Speter      return SVN_NO_ERROR;
237251881Speter    }
238251881Speter  SVN_ERR(err);
239251881Speter
240251881Speter  /* Read the proplist for THIS_DIR. */
241251881Speter  SVN_ERR(read_one_proplist(*all_wcprops, SVN_WC_ENTRY_THIS_DIR, stream,
242251881Speter                            result_pool, scratch_pool));
243251881Speter
244251881Speter  /* And now, the children. */
245251881Speter  while (1729)
246251881Speter    {
247251881Speter      svn_stringbuf_t *line;
248251881Speter      svn_boolean_t eof;
249251881Speter
250251881Speter      SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, result_pool));
251251881Speter      if (eof)
252251881Speter        {
253251881Speter          if (line->len > 0)
254251881Speter            return svn_error_createf
255251881Speter              (SVN_ERR_WC_CORRUPT, NULL,
256251881Speter               _("Missing end of line in wcprops file for '%s'"),
257251881Speter               svn_dirent_local_style(dir_abspath, scratch_pool));
258251881Speter          break;
259251881Speter        }
260251881Speter      SVN_ERR(read_one_proplist(*all_wcprops, line->data, stream,
261251881Speter                                result_pool, scratch_pool));
262251881Speter    }
263251881Speter
264251881Speter  return svn_error_trace(svn_stream_close(stream));
265251881Speter}
266251881Speter
267251881Speter/* Return in CHILDREN, the list of all 1.6 versioned subdirectories
268251881Speter   which also exist on disk as directories.
269251881Speter
270251881Speter   If DELETE_DIR is not NULL set *DELETE_DIR to TRUE if the directory
271251881Speter   should be deleted after migrating to WC-NG, otherwise to FALSE.
272251881Speter
273251881Speter   If SKIP_MISSING is TRUE, don't add missing or obstructed subdirectories
274251881Speter   to the list of children.
275251881Speter   */
276251881Speterstatic svn_error_t *
277251881Speterget_versioned_subdirs(apr_array_header_t **children,
278251881Speter                      svn_boolean_t *delete_dir,
279251881Speter                      const char *dir_abspath,
280251881Speter                      svn_boolean_t skip_missing,
281251881Speter                      apr_pool_t *result_pool,
282251881Speter                      apr_pool_t *scratch_pool)
283251881Speter{
284251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
285251881Speter  apr_hash_t *entries;
286251881Speter  apr_hash_index_t *hi;
287251881Speter  svn_wc_entry_t *this_dir = NULL;
288251881Speter
289251881Speter  *children = apr_array_make(result_pool, 10, sizeof(const char *));
290251881Speter
291251881Speter  SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath,
292251881Speter                                   scratch_pool, iterpool));
293251881Speter  for (hi = apr_hash_first(scratch_pool, entries);
294251881Speter       hi;
295251881Speter       hi = apr_hash_next(hi))
296251881Speter    {
297289180Speter      const char *name = apr_hash_this_key(hi);
298289180Speter      const svn_wc_entry_t *entry = apr_hash_this_val(hi);
299251881Speter      const char *child_abspath;
300251881Speter      svn_boolean_t hidden;
301251881Speter
302251881Speter      /* skip "this dir"  */
303251881Speter      if (*name == '\0')
304251881Speter        {
305289180Speter          this_dir = apr_hash_this_val(hi);
306251881Speter          continue;
307251881Speter        }
308251881Speter      else if (entry->kind != svn_node_dir)
309251881Speter        continue;
310251881Speter
311251881Speter      svn_pool_clear(iterpool);
312251881Speter
313251881Speter      /* If a directory is 'hidden' skip it as subdir */
314251881Speter      SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry));
315251881Speter      if (hidden)
316251881Speter        continue;
317251881Speter
318251881Speter      child_abspath = svn_dirent_join(dir_abspath, name, scratch_pool);
319251881Speter
320251881Speter      if (skip_missing)
321251881Speter        {
322251881Speter          svn_node_kind_t kind;
323251881Speter          SVN_ERR(svn_io_check_path(child_abspath, &kind, scratch_pool));
324251881Speter
325251881Speter          if (kind != svn_node_dir)
326251881Speter            continue;
327251881Speter        }
328251881Speter
329251881Speter      APR_ARRAY_PUSH(*children, const char *) = apr_pstrdup(result_pool,
330251881Speter                                                            child_abspath);
331251881Speter    }
332251881Speter
333251881Speter  svn_pool_destroy(iterpool);
334251881Speter
335251881Speter  if (delete_dir != NULL)
336251881Speter    {
337251881Speter      *delete_dir = (this_dir != NULL)
338251881Speter                     && (this_dir->schedule == svn_wc_schedule_delete)
339251881Speter                     && ! this_dir->keep_local;
340251881Speter    }
341251881Speter
342251881Speter  return SVN_NO_ERROR;
343251881Speter}
344251881Speter
345251881Speter
346251881Speter/* Return in CHILDREN the names of all versioned *files* in SDB that
347251881Speter   are children of PARENT_RELPATH.  These files' existence on disk is
348251881Speter   not tested.
349251881Speter
350251881Speter   This set of children is intended for property upgrades.
351251881Speter   Subdirectory's properties exist in the subdirs.
352251881Speter
353251881Speter   Note that this uses just the SDB to locate children, which means
354251881Speter   that the children must have been upgraded to wc-ng format. */
355251881Speterstatic svn_error_t *
356251881Speterget_versioned_files(const apr_array_header_t **children,
357251881Speter                    const char *parent_relpath,
358251881Speter                    svn_sqlite__db_t *sdb,
359251881Speter                    apr_int64_t wc_id,
360251881Speter                    apr_pool_t *result_pool,
361251881Speter                    apr_pool_t *scratch_pool)
362251881Speter{
363251881Speter  svn_sqlite__stmt_t *stmt;
364251881Speter  apr_array_header_t *child_names;
365251881Speter  svn_boolean_t have_row;
366251881Speter
367251881Speter  /* ### just select 'file' children. do we need 'symlink' in the future?  */
368251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ALL_FILES));
369251881Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
370251881Speter
371251881Speter  /* ### 10 is based on Subversion's average of 8.5 files per versioned
372251881Speter     ### directory in its repository. maybe use a different value? or
373251881Speter     ### count rows first?  */
374251881Speter  child_names = apr_array_make(result_pool, 10, sizeof(const char *));
375251881Speter
376251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
377251881Speter  while (have_row)
378251881Speter    {
379251881Speter      const char *local_relpath = svn_sqlite__column_text(stmt, 0,
380251881Speter                                                          result_pool);
381251881Speter
382251881Speter      APR_ARRAY_PUSH(child_names, const char *)
383251881Speter        = svn_relpath_basename(local_relpath, result_pool);
384251881Speter
385251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
386251881Speter    }
387251881Speter
388251881Speter  *children = child_names;
389251881Speter
390251881Speter  return svn_error_trace(svn_sqlite__reset(stmt));
391251881Speter}
392251881Speter
393251881Speter
394251881Speter/* Return the path of the old-school administrative lock file
395251881Speter   associated with LOCAL_DIR_ABSPATH, allocated from RESULT_POOL. */
396251881Speterstatic const char *
397251881Speterbuild_lockfile_path(const char *local_dir_abspath,
398251881Speter                    apr_pool_t *result_pool)
399251881Speter{
400251881Speter  return svn_dirent_join_many(result_pool,
401251881Speter                              local_dir_abspath,
402251881Speter                              svn_wc_get_adm_dir(result_pool),
403251881Speter                              ADM_LOCK,
404289180Speter                              SVN_VA_NULL);
405251881Speter}
406251881Speter
407251881Speter
408251881Speter/* Create a physical lock file in the admin directory for ABSPATH.  */
409251881Speterstatic svn_error_t *
410251881Spetercreate_physical_lock(const char *abspath, apr_pool_t *scratch_pool)
411251881Speter{
412251881Speter  const char *lock_abspath = build_lockfile_path(abspath, scratch_pool);
413251881Speter  svn_error_t *err;
414251881Speter  apr_file_t *file;
415251881Speter
416251881Speter  err = svn_io_file_open(&file, lock_abspath,
417251881Speter                         APR_WRITE | APR_CREATE | APR_EXCL,
418251881Speter                         APR_OS_DEFAULT,
419251881Speter                         scratch_pool);
420251881Speter
421251881Speter  if (err && APR_STATUS_IS_EEXIST(err->apr_err))
422251881Speter    {
423251881Speter      /* Congratulations, we just stole a physical lock from somebody */
424251881Speter      svn_error_clear(err);
425251881Speter      return SVN_NO_ERROR;
426251881Speter    }
427251881Speter
428251881Speter  return svn_error_trace(err);
429251881Speter}
430251881Speter
431251881Speter
432251881Speter/* Wipe out all the obsolete files/dirs from the administrative area.  */
433251881Speterstatic void
434251881Speterwipe_obsolete_files(const char *wcroot_abspath, apr_pool_t *scratch_pool)
435251881Speter{
436251881Speter  /* Zap unused files.  */
437251881Speter  svn_error_clear(svn_io_remove_file2(
438251881Speter                    svn_wc__adm_child(wcroot_abspath,
439251881Speter                                      SVN_WC__ADM_FORMAT,
440251881Speter                                      scratch_pool),
441251881Speter                    TRUE, scratch_pool));
442251881Speter  svn_error_clear(svn_io_remove_file2(
443251881Speter                    svn_wc__adm_child(wcroot_abspath,
444251881Speter                                      SVN_WC__ADM_ENTRIES,
445251881Speter                                      scratch_pool),
446251881Speter                    TRUE, scratch_pool));
447251881Speter  svn_error_clear(svn_io_remove_file2(
448251881Speter                    svn_wc__adm_child(wcroot_abspath,
449251881Speter                                      ADM_EMPTY_FILE,
450251881Speter                                      scratch_pool),
451251881Speter                    TRUE, scratch_pool));
452251881Speter  svn_error_clear(svn_io_remove_file2(
453251881Speter                    svn_wc__adm_child(wcroot_abspath,
454251881Speter                                      ADM_README,
455251881Speter                                      scratch_pool),
456251881Speter                    TRUE, scratch_pool));
457251881Speter
458251881Speter  /* For formats <= SVN_WC__WCPROPS_MANY_FILES_VERSION, we toss the wcprops
459251881Speter     for the directory itself, and then all the wcprops for the files.  */
460251881Speter  svn_error_clear(svn_io_remove_file2(
461251881Speter                    svn_wc__adm_child(wcroot_abspath,
462251881Speter                                      WCPROPS_FNAME_FOR_DIR,
463251881Speter                                      scratch_pool),
464251881Speter                    TRUE, scratch_pool));
465251881Speter  svn_error_clear(svn_io_remove_dir2(
466251881Speter                    svn_wc__adm_child(wcroot_abspath,
467251881Speter                                      WCPROPS_SUBDIR_FOR_FILES,
468251881Speter                                      scratch_pool),
469251881Speter                    FALSE, NULL, NULL, scratch_pool));
470251881Speter
471251881Speter  /* And for later formats, they are aggregated into one file.  */
472251881Speter  svn_error_clear(svn_io_remove_file2(
473251881Speter                    svn_wc__adm_child(wcroot_abspath,
474251881Speter                                      WCPROPS_ALL_DATA,
475251881Speter                                      scratch_pool),
476251881Speter                    TRUE, scratch_pool));
477251881Speter
478251881Speter  /* Remove the old text-base directory and the old text-base files. */
479251881Speter  svn_error_clear(svn_io_remove_dir2(
480251881Speter                    svn_wc__adm_child(wcroot_abspath,
481251881Speter                                      TEXT_BASE_SUBDIR,
482251881Speter                                      scratch_pool),
483251881Speter                    FALSE, NULL, NULL, scratch_pool));
484251881Speter
485251881Speter  /* Remove the old properties files... whole directories at a time.  */
486251881Speter  svn_error_clear(svn_io_remove_dir2(
487251881Speter                    svn_wc__adm_child(wcroot_abspath,
488251881Speter                                      PROPS_SUBDIR,
489251881Speter                                      scratch_pool),
490251881Speter                    FALSE, NULL, NULL, scratch_pool));
491251881Speter  svn_error_clear(svn_io_remove_dir2(
492251881Speter                    svn_wc__adm_child(wcroot_abspath,
493251881Speter                                      PROP_BASE_SUBDIR,
494251881Speter                                      scratch_pool),
495251881Speter                    FALSE, NULL, NULL, scratch_pool));
496251881Speter  svn_error_clear(svn_io_remove_file2(
497251881Speter                     svn_wc__adm_child(wcroot_abspath,
498251881Speter                                       PROP_WORKING_FOR_DIR,
499251881Speter                                       scratch_pool),
500251881Speter                     TRUE, scratch_pool));
501251881Speter  svn_error_clear(svn_io_remove_file2(
502251881Speter                     svn_wc__adm_child(wcroot_abspath,
503251881Speter                                      PROP_BASE_FOR_DIR,
504251881Speter                                      scratch_pool),
505251881Speter                     TRUE, scratch_pool));
506251881Speter  svn_error_clear(svn_io_remove_file2(
507251881Speter                     svn_wc__adm_child(wcroot_abspath,
508251881Speter                                      PROP_REVERT_FOR_DIR,
509251881Speter                                      scratch_pool),
510251881Speter                     TRUE, scratch_pool));
511251881Speter
512251881Speter#if 0
513251881Speter  /* ### this checks for a write-lock, and we are not (always) taking out
514251881Speter     ### a write lock in all callers.  */
515251881Speter  SVN_ERR(svn_wc__adm_cleanup_tmp_area(db, wcroot_abspath, iterpool));
516251881Speter#endif
517251881Speter
518251881Speter  /* Remove the old-style lock file LAST.  */
519251881Speter  svn_error_clear(svn_io_remove_file2(
520251881Speter                    build_lockfile_path(wcroot_abspath, scratch_pool),
521251881Speter                    TRUE, scratch_pool));
522251881Speter}
523251881Speter
524251881Spetersvn_error_t *
525251881Spetersvn_wc__wipe_postupgrade(const char *dir_abspath,
526251881Speter                         svn_boolean_t whole_admin,
527251881Speter                         svn_cancel_func_t cancel_func,
528251881Speter                         void *cancel_baton,
529251881Speter                         apr_pool_t *scratch_pool)
530251881Speter{
531251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
532251881Speter  apr_array_header_t *subdirs;
533251881Speter  svn_error_t *err;
534251881Speter  svn_boolean_t delete_dir;
535251881Speter  int i;
536251881Speter
537251881Speter  if (cancel_func)
538362181Sdim    SVN_ERR(cancel_func(cancel_baton));
539251881Speter
540251881Speter  err = get_versioned_subdirs(&subdirs, &delete_dir, dir_abspath, TRUE,
541251881Speter                              scratch_pool, iterpool);
542251881Speter  if (err)
543251881Speter    {
544251881Speter      if (APR_STATUS_IS_ENOENT(err->apr_err))
545251881Speter        {
546251881Speter          /* An unversioned dir is obstructing a versioned dir */
547251881Speter          svn_error_clear(err);
548251881Speter          err = NULL;
549251881Speter        }
550251881Speter      svn_pool_destroy(iterpool);
551251881Speter      return svn_error_trace(err);
552251881Speter    }
553251881Speter  for (i = 0; i < subdirs->nelts; ++i)
554251881Speter    {
555251881Speter      const char *child_abspath = APR_ARRAY_IDX(subdirs, i, const char *);
556251881Speter
557251881Speter      svn_pool_clear(iterpool);
558251881Speter      SVN_ERR(svn_wc__wipe_postupgrade(child_abspath, TRUE,
559251881Speter                                       cancel_func, cancel_baton, iterpool));
560251881Speter    }
561251881Speter
562251881Speter  /* ### Should we really be ignoring errors here? */
563251881Speter  if (whole_admin)
564251881Speter    svn_error_clear(svn_io_remove_dir2(svn_wc__adm_child(dir_abspath, "",
565251881Speter                                                         iterpool),
566251881Speter                                       TRUE, NULL, NULL, iterpool));
567251881Speter  else
568251881Speter    wipe_obsolete_files(dir_abspath, scratch_pool);
569251881Speter
570251881Speter  if (delete_dir)
571251881Speter    {
572251881Speter      /* If this was a WC-NG single database copy, this directory wouldn't
573251881Speter         be here (unless it was deleted with --keep-local)
574251881Speter
575251881Speter         If the directory is empty, we can just delete it; if not we
576251881Speter         keep it.
577251881Speter       */
578251881Speter      svn_error_clear(svn_io_dir_remove_nonrecursive(dir_abspath, iterpool));
579251881Speter    }
580251881Speter
581251881Speter  svn_pool_destroy(iterpool);
582251881Speter
583251881Speter  return SVN_NO_ERROR;
584251881Speter}
585251881Speter
586251881Speter/* Ensure that ENTRY has its REPOS and UUID fields set. These will be
587251881Speter   used to establish the REPOSITORY row in the new database, and then
588251881Speter   used within the upgraded entries as they are written into the database.
589251881Speter
590251881Speter   If one or both are not available, then it attempts to retrieve this
591251881Speter   information from REPOS_CACHE. And if that fails from REPOS_INFO_FUNC,
592251881Speter   passing REPOS_INFO_BATON.
593251881Speter   Returns a user understandable error using LOCAL_ABSPATH if the
594251881Speter   information cannot be obtained.  */
595251881Speterstatic svn_error_t *
596251881Speterensure_repos_info(svn_wc_entry_t *entry,
597251881Speter                  const char *local_abspath,
598251881Speter                  svn_wc_upgrade_get_repos_info_t repos_info_func,
599251881Speter                  void *repos_info_baton,
600251881Speter                  apr_hash_t *repos_cache,
601251881Speter                  apr_pool_t *result_pool,
602251881Speter                  apr_pool_t *scratch_pool)
603251881Speter{
604251881Speter  /* Easy exit.  */
605251881Speter  if (entry->repos != NULL && entry->uuid != NULL)
606251881Speter    return SVN_NO_ERROR;
607251881Speter
608251881Speter  if ((entry->repos == NULL || entry->uuid == NULL)
609251881Speter      && entry->url)
610251881Speter    {
611251881Speter      apr_hash_index_t *hi;
612251881Speter
613251881Speter      for (hi = apr_hash_first(scratch_pool, repos_cache);
614251881Speter           hi; hi = apr_hash_next(hi))
615251881Speter        {
616289180Speter          if (svn_uri__is_ancestor(apr_hash_this_key(hi), entry->url))
617251881Speter            {
618251881Speter              if (!entry->repos)
619289180Speter                entry->repos = apr_hash_this_key(hi);
620251881Speter
621251881Speter              if (!entry->uuid)
622289180Speter                entry->uuid = apr_hash_this_val(hi);
623251881Speter
624251881Speter              return SVN_NO_ERROR;
625251881Speter            }
626251881Speter        }
627251881Speter    }
628251881Speter
629251881Speter  if (entry->repos == NULL && repos_info_func == NULL)
630251881Speter    return svn_error_createf(
631251881Speter        SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
632251881Speter        _("Working copy '%s' can't be upgraded because the repository root is "
633251881Speter          "not available and can't be retrieved"),
634251881Speter        svn_dirent_local_style(local_abspath, scratch_pool));
635251881Speter
636251881Speter  if (entry->uuid == NULL && repos_info_func == NULL)
637251881Speter    return svn_error_createf(
638251881Speter        SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
639251881Speter        _("Working copy '%s' can't be upgraded because the repository uuid is "
640251881Speter          "not available and can't be retrieved"),
641251881Speter        svn_dirent_local_style(local_abspath, scratch_pool));
642251881Speter
643251881Speter   if (entry->url == NULL)
644251881Speter     return svn_error_createf(
645251881Speter        SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
646251881Speter        _("Working copy '%s' can't be upgraded because it doesn't have a url"),
647251881Speter        svn_dirent_local_style(local_abspath, scratch_pool));
648251881Speter
649251881Speter   return svn_error_trace((*repos_info_func)(&entry->repos, &entry->uuid,
650251881Speter                                             repos_info_baton,
651251881Speter                                             entry->url,
652251881Speter                                             result_pool, scratch_pool));
653251881Speter}
654251881Speter
655251881Speter
656289180Speter/* ### need much more docco
657251881Speter
658289180Speter   ### this function should be called within a sqlite transaction. it makes
659289180Speter   ### assumptions around this fact.
660289180Speter
661289180Speter   Apply the various sets of properties to the database nodes based on
662289180Speter   their existence/presence, the current state of the node, and the original
663289180Speter   format of the working copy which provided these property sets.
664289180Speter*/
665289180Speterstatic svn_error_t *
666289180Speterupgrade_apply_props(svn_sqlite__db_t *sdb,
667289180Speter                    const char *dir_abspath,
668289180Speter                    const char *local_relpath,
669289180Speter                    apr_hash_t *base_props,
670289180Speter                    apr_hash_t *revert_props,
671289180Speter                    apr_hash_t *working_props,
672289180Speter                    int original_format,
673289180Speter                    apr_int64_t wc_id,
674289180Speter                    apr_pool_t *scratch_pool)
675289180Speter{
676289180Speter  svn_sqlite__stmt_t *stmt;
677289180Speter  svn_boolean_t have_row;
678289180Speter  int top_op_depth = -1;
679289180Speter  int below_op_depth = -1;
680289180Speter  svn_wc__db_status_t top_presence;
681289180Speter  svn_wc__db_status_t below_presence;
682289180Speter  int affected_rows;
683289180Speter
684289180Speter  /* ### working_props: use set_props_txn.
685289180Speter     ### if working_props == NULL, then skip. what if they equal the
686289180Speter     ### pristine props? we should probably do the compare here.
687289180Speter     ###
688289180Speter     ### base props go into WORKING_NODE if avail, otherwise BASE.
689289180Speter     ###
690289180Speter     ### revert only goes into BASE. (and WORKING better be there!)
691289180Speter
692289180Speter     Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
693289180Speter     file was deleted, then a copy (potentially with props) was disallowed
694289180Speter     and could not replace the deletion. An addition *could* be performed,
695289180Speter     but that would never bring its own props.
696289180Speter
697289180Speter     1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
698289180Speter     bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
699289180Speter     construct a REVERT_PROPS if the target had no props. Thus, reverting
700289180Speter     the delete/copy would see no REVERT_PROPS to restore, leaving the
701289180Speter     props from the copy source intact, and appearing as if they are (now)
702289180Speter     the base props for the previously-deleted file. (wc corruption)
703289180Speter
704289180Speter     1.4.6 ensured that an empty REVERT_PROPS would be established at all
705289180Speter     times. See issue 2530, and r861670 as starting points.
706289180Speter
707289180Speter     We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
708289180Speter     the handling of our inputs, relative to the state of this node.
709289180Speter  */
710289180Speter
711289180Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
712289180Speter  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
713289180Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
714289180Speter  if (have_row)
715289180Speter    {
716289180Speter      top_op_depth = svn_sqlite__column_int(stmt, 0);
717289180Speter      top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
718289180Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
719289180Speter      if (have_row)
720289180Speter        {
721289180Speter          below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
722289180Speter
723289180Speter          /* There might be an intermediate layer on mixed-revision copies,
724289180Speter             or when BASE is shadowed */
725289180Speter          if (below_presence == svn_wc__db_status_not_present
726289180Speter              || below_presence == svn_wc__db_status_deleted)
727289180Speter            SVN_ERR(svn_sqlite__step(&have_row, stmt));
728289180Speter
729289180Speter          if (have_row)
730289180Speter            {
731289180Speter              below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
732289180Speter              below_op_depth = svn_sqlite__column_int(stmt, 0);
733289180Speter            }
734289180Speter        }
735289180Speter    }
736289180Speter  SVN_ERR(svn_sqlite__reset(stmt));
737289180Speter
738289180Speter  /* Detect the buggy scenario described above. We cannot upgrade this
739289180Speter     working copy if we have no idea where BASE_PROPS should go.  */
740289180Speter  if (original_format > SVN_WC__NO_REVERT_FILES
741289180Speter      && revert_props == NULL
742289180Speter      && top_op_depth != -1
743289180Speter      && top_presence == svn_wc__db_status_normal
744289180Speter      && below_op_depth != -1
745289180Speter      && below_presence != svn_wc__db_status_not_present)
746289180Speter    {
747289180Speter      /* There should be REVERT_PROPS, so it appears that we just ran into
748289180Speter         the described bug. Sigh.  */
749289180Speter      return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
750289180Speter                               _("The properties of '%s' are in an "
751289180Speter                                 "indeterminate state and cannot be "
752289180Speter                                 "upgraded. See issue #2530."),
753289180Speter                               svn_dirent_local_style(
754289180Speter                                 svn_dirent_join(dir_abspath, local_relpath,
755289180Speter                                                 scratch_pool), scratch_pool));
756289180Speter    }
757289180Speter
758289180Speter  /* Need at least one row, or two rows if there are revert props */
759289180Speter  if (top_op_depth == -1
760289180Speter      || (below_op_depth == -1 && revert_props))
761289180Speter    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
762289180Speter                             _("Insufficient NODES rows for '%s'"),
763289180Speter                             svn_dirent_local_style(
764289180Speter                               svn_dirent_join(dir_abspath, local_relpath,
765289180Speter                                               scratch_pool), scratch_pool));
766289180Speter
767289180Speter  /* one row, base props only: upper row gets base props
768289180Speter     two rows, base props only: lower row gets base props
769289180Speter     two rows, revert props only: lower row gets revert props
770289180Speter     two rows, base and revert props: upper row gets base, lower gets revert */
771289180Speter
772289180Speter
773289180Speter  if (revert_props || below_op_depth == -1)
774289180Speter    {
775289180Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
776289180Speter                                        STMT_UPDATE_NODE_PROPS));
777289180Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
778289180Speter                                wc_id, local_relpath, top_op_depth));
779289180Speter      SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
780289180Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
781289180Speter
782289180Speter      SVN_ERR_ASSERT(affected_rows == 1);
783289180Speter    }
784289180Speter
785289180Speter  if (below_op_depth != -1)
786289180Speter    {
787289180Speter      apr_hash_t *props = revert_props ? revert_props : base_props;
788289180Speter
789289180Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
790289180Speter                                        STMT_UPDATE_NODE_PROPS));
791289180Speter      SVN_ERR(svn_sqlite__bindf(stmt, "isd",
792289180Speter                                wc_id, local_relpath, below_op_depth));
793289180Speter      SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
794289180Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
795289180Speter
796289180Speter      SVN_ERR_ASSERT(affected_rows == 1);
797289180Speter    }
798289180Speter
799289180Speter  /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE.  */
800289180Speter  if (working_props != NULL
801289180Speter      && base_props != NULL)
802289180Speter    {
803289180Speter      apr_array_header_t *diffs;
804289180Speter
805289180Speter      SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
806289180Speter
807289180Speter      if (diffs->nelts == 0)
808289180Speter        working_props = NULL; /* No differences */
809289180Speter    }
810289180Speter
811289180Speter  if (working_props != NULL)
812289180Speter    {
813289180Speter      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
814289180Speter                                  STMT_UPDATE_ACTUAL_PROPS));
815289180Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
816289180Speter      SVN_ERR(svn_sqlite__bind_properties(stmt, 3, working_props,
817289180Speter                                          scratch_pool));
818289180Speter      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
819289180Speter
820289180Speter      if (affected_rows == 0)
821289180Speter        {
822289180Speter          /* We have to insert a row in ACTUAL */
823289180Speter
824289180Speter          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
825289180Speter                                            STMT_INSERT_ACTUAL_PROPS));
826289180Speter          SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
827289180Speter          if (*local_relpath != '\0')
828289180Speter            SVN_ERR(svn_sqlite__bind_text(stmt, 3,
829289180Speter                                          svn_relpath_dirname(local_relpath,
830289180Speter                                                              scratch_pool)));
831289180Speter          SVN_ERR(svn_sqlite__bind_properties(stmt, 4, working_props,
832289180Speter                                              scratch_pool));
833289180Speter          return svn_error_trace(svn_sqlite__step_done(stmt));
834289180Speter        }
835289180Speter    }
836289180Speter
837289180Speter  return SVN_NO_ERROR;
838289180Speter}
839289180Speter
840289180Speter
841251881Speterstruct bump_baton {
842251881Speter  const char *wcroot_abspath;
843251881Speter};
844251881Speter
845251881Speter/* Migrate the properties for one node (LOCAL_ABSPATH).  */
846251881Speterstatic svn_error_t *
847251881Spetermigrate_node_props(const char *dir_abspath,
848251881Speter                   const char *new_wcroot_abspath,
849251881Speter                   const char *name,
850251881Speter                   svn_sqlite__db_t *sdb,
851251881Speter                   int original_format,
852251881Speter                   apr_int64_t wc_id,
853251881Speter                   apr_pool_t *scratch_pool)
854251881Speter{
855251881Speter  const char *base_abspath;  /* old name. nowadays: "pristine"  */
856251881Speter  const char *revert_abspath;  /* old name. nowadays: "BASE"  */
857251881Speter  const char *working_abspath;  /* old name. nowadays: "ACTUAL"  */
858251881Speter  apr_hash_t *base_props;
859251881Speter  apr_hash_t *revert_props;
860251881Speter  apr_hash_t *working_props;
861251881Speter  const char *old_wcroot_abspath
862251881Speter    = svn_dirent_get_longest_ancestor(dir_abspath, new_wcroot_abspath,
863251881Speter                                      scratch_pool);
864251881Speter  const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath,
865251881Speter                                                     dir_abspath);
866251881Speter
867251881Speter  if (*name == '\0')
868251881Speter    {
869251881Speter      base_abspath = svn_wc__adm_child(dir_abspath,
870251881Speter                                       PROP_BASE_FOR_DIR, scratch_pool);
871251881Speter      revert_abspath = svn_wc__adm_child(dir_abspath,
872251881Speter                                         PROP_REVERT_FOR_DIR, scratch_pool);
873251881Speter      working_abspath = svn_wc__adm_child(dir_abspath,
874251881Speter                                          PROP_WORKING_FOR_DIR, scratch_pool);
875251881Speter    }
876251881Speter  else
877251881Speter    {
878251881Speter      const char *basedir_abspath;
879251881Speter      const char *propsdir_abspath;
880251881Speter
881251881Speter      propsdir_abspath = svn_wc__adm_child(dir_abspath, PROPS_SUBDIR,
882251881Speter                                           scratch_pool);
883251881Speter      basedir_abspath = svn_wc__adm_child(dir_abspath, PROP_BASE_SUBDIR,
884251881Speter                                          scratch_pool);
885251881Speter
886251881Speter      base_abspath = svn_dirent_join(basedir_abspath,
887251881Speter                                     apr_pstrcat(scratch_pool,
888251881Speter                                                 name,
889251881Speter                                                 SVN_WC__BASE_EXT,
890289180Speter                                                 SVN_VA_NULL),
891251881Speter                                     scratch_pool);
892251881Speter
893251881Speter      revert_abspath = svn_dirent_join(basedir_abspath,
894251881Speter                                       apr_pstrcat(scratch_pool,
895251881Speter                                                   name,
896251881Speter                                                   SVN_WC__REVERT_EXT,
897289180Speter                                                   SVN_VA_NULL),
898251881Speter                                       scratch_pool);
899251881Speter
900251881Speter      working_abspath = svn_dirent_join(propsdir_abspath,
901251881Speter                                        apr_pstrcat(scratch_pool,
902251881Speter                                                    name,
903251881Speter                                                    SVN_WC__WORK_EXT,
904289180Speter                                                    SVN_VA_NULL),
905251881Speter                                        scratch_pool);
906251881Speter    }
907251881Speter
908251881Speter  SVN_ERR(read_propfile(&base_props, base_abspath,
909251881Speter                        scratch_pool, scratch_pool));
910251881Speter  SVN_ERR(read_propfile(&revert_props, revert_abspath,
911251881Speter                        scratch_pool, scratch_pool));
912251881Speter  SVN_ERR(read_propfile(&working_props, working_abspath,
913251881Speter                        scratch_pool, scratch_pool));
914251881Speter
915289180Speter  return svn_error_trace(upgrade_apply_props(
916251881Speter                            sdb, new_wcroot_abspath,
917251881Speter                            svn_relpath_join(dir_relpath, name, scratch_pool),
918251881Speter                            base_props, revert_props, working_props,
919251881Speter                            original_format, wc_id,
920251881Speter                            scratch_pool));
921251881Speter}
922251881Speter
923251881Speter
924251881Speter/* */
925251881Speterstatic svn_error_t *
926251881Spetermigrate_props(const char *dir_abspath,
927251881Speter              const char *new_wcroot_abspath,
928251881Speter              svn_sqlite__db_t *sdb,
929251881Speter              int original_format,
930251881Speter              apr_int64_t wc_id,
931251881Speter              apr_pool_t *scratch_pool)
932251881Speter{
933251881Speter  /* General logic here: iterate over all the immediate children of the root
934251881Speter     (since we aren't yet in a centralized system), and for any properties that
935251881Speter     exist, map them as follows:
936251881Speter
937251881Speter     if (revert props exist):
938251881Speter       revert  -> BASE
939251881Speter       base    -> WORKING
940251881Speter       working -> ACTUAL
941251881Speter     else if (prop pristine is working [as defined in props.c] ):
942251881Speter       base    -> WORKING
943251881Speter       working -> ACTUAL
944251881Speter     else:
945251881Speter       base    -> BASE
946251881Speter       working -> ACTUAL
947251881Speter
948251881Speter     ### the middle "test" should simply look for a WORKING_NODE row
949251881Speter
950251881Speter     Note that it is legal for "working" props to be missing. That implies
951251881Speter     no local changes to the properties.
952251881Speter  */
953251881Speter  const apr_array_header_t *children;
954251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
955251881Speter  const char *old_wcroot_abspath
956251881Speter    = svn_dirent_get_longest_ancestor(dir_abspath, new_wcroot_abspath,
957251881Speter                                      scratch_pool);
958251881Speter  const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath,
959251881Speter                                                     dir_abspath);
960251881Speter  int i;
961251881Speter
962251881Speter  /* Migrate the props for "this dir".  */
963251881Speter  SVN_ERR(migrate_node_props(dir_abspath, new_wcroot_abspath, "", sdb,
964251881Speter                             original_format, wc_id, iterpool));
965251881Speter
966251881Speter  /* Iterate over all the files in this SDB.  */
967251881Speter  SVN_ERR(get_versioned_files(&children, dir_relpath, sdb, wc_id, scratch_pool,
968251881Speter                              iterpool));
969251881Speter  for (i = 0; i < children->nelts; i++)
970251881Speter    {
971251881Speter      const char *name = APR_ARRAY_IDX(children, i, const char *);
972251881Speter
973251881Speter      svn_pool_clear(iterpool);
974251881Speter
975251881Speter      SVN_ERR(migrate_node_props(dir_abspath, new_wcroot_abspath,
976251881Speter                                 name, sdb, original_format, wc_id, iterpool));
977251881Speter    }
978251881Speter
979251881Speter  svn_pool_destroy(iterpool);
980251881Speter
981251881Speter  return SVN_NO_ERROR;
982251881Speter}
983251881Speter
984251881Speter
985251881Speter/* If STR ends with SUFFIX and is longer than SUFFIX, return the part of
986251881Speter * STR that comes before SUFFIX; else return NULL. */
987251881Speterstatic char *
988251881Speterremove_suffix(const char *str, const char *suffix, apr_pool_t *result_pool)
989251881Speter{
990251881Speter  size_t str_len = strlen(str);
991251881Speter  size_t suffix_len = strlen(suffix);
992251881Speter
993251881Speter  if (str_len > suffix_len
994251881Speter      && strcmp(str + str_len - suffix_len, suffix) == 0)
995251881Speter    {
996251881Speter      return apr_pstrmemdup(result_pool, str, str_len - suffix_len);
997251881Speter    }
998251881Speter
999251881Speter  return NULL;
1000251881Speter}
1001251881Speter
1002251881Speter/* Copy all the text-base files from the administrative area of WC directory
1003251881Speter   DIR_ABSPATH into the pristine store of SDB which is located in directory
1004251881Speter   NEW_WCROOT_ABSPATH.
1005251881Speter
1006251881Speter   Set *TEXT_BASES_INFO to a new hash, allocated in RESULT_POOL, that maps
1007251881Speter   (const char *) name of the versioned file to (svn_wc__text_base_info_t *)
1008251881Speter   information about the pristine text. */
1009251881Speterstatic svn_error_t *
1010251881Spetermigrate_text_bases(apr_hash_t **text_bases_info,
1011251881Speter                   const char *dir_abspath,
1012251881Speter                   const char *new_wcroot_abspath,
1013251881Speter                   svn_sqlite__db_t *sdb,
1014251881Speter                   apr_pool_t *result_pool,
1015251881Speter                   apr_pool_t *scratch_pool)
1016251881Speter{
1017251881Speter  apr_hash_t *dirents;
1018251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1019251881Speter  apr_hash_index_t *hi;
1020251881Speter  const char *text_base_dir = svn_wc__adm_child(dir_abspath,
1021251881Speter                                                TEXT_BASE_SUBDIR,
1022251881Speter                                                scratch_pool);
1023251881Speter
1024251881Speter  *text_bases_info = apr_hash_make(result_pool);
1025251881Speter
1026251881Speter  /* Iterate over the text-base files */
1027251881Speter  SVN_ERR(svn_io_get_dirents3(&dirents, text_base_dir, TRUE,
1028251881Speter                              scratch_pool, scratch_pool));
1029251881Speter  for (hi = apr_hash_first(scratch_pool, dirents); hi;
1030251881Speter       hi = apr_hash_next(hi))
1031251881Speter    {
1032289180Speter      const char *text_base_basename = apr_hash_this_key(hi);
1033251881Speter      svn_checksum_t *md5_checksum;
1034251881Speter      svn_checksum_t *sha1_checksum;
1035251881Speter
1036251881Speter      svn_pool_clear(iterpool);
1037251881Speter
1038251881Speter      /* Calculate its checksums and copy it to the pristine store */
1039251881Speter      {
1040251881Speter        const char *pristine_path;
1041251881Speter        const char *text_base_path;
1042251881Speter        const char *temp_path;
1043251881Speter        svn_sqlite__stmt_t *stmt;
1044251881Speter        apr_finfo_t finfo;
1045251881Speter        svn_stream_t *read_stream;
1046251881Speter        svn_stream_t *result_stream;
1047251881Speter
1048251881Speter        text_base_path = svn_dirent_join(text_base_dir, text_base_basename,
1049251881Speter                                         iterpool);
1050251881Speter
1051251881Speter        /* Create a copy and calculate a checksum in one step */
1052251881Speter        SVN_ERR(svn_stream_open_unique(&result_stream, &temp_path,
1053251881Speter                                       new_wcroot_abspath,
1054251881Speter                                       svn_io_file_del_none,
1055251881Speter                                       iterpool, iterpool));
1056251881Speter
1057251881Speter        SVN_ERR(svn_stream_open_readonly(&read_stream, text_base_path,
1058251881Speter                                           iterpool, iterpool));
1059251881Speter
1060251881Speter        read_stream = svn_stream_checksummed2(read_stream, &md5_checksum,
1061251881Speter                                              NULL, svn_checksum_md5,
1062251881Speter                                              TRUE, iterpool);
1063251881Speter
1064251881Speter        read_stream = svn_stream_checksummed2(read_stream, &sha1_checksum,
1065251881Speter                                              NULL, svn_checksum_sha1,
1066251881Speter                                              TRUE, iterpool);
1067251881Speter
1068251881Speter        /* This calculates the hash, creates a copy and closes the stream */
1069251881Speter        SVN_ERR(svn_stream_copy3(read_stream, result_stream,
1070251881Speter                                 NULL, NULL, iterpool));
1071251881Speter
1072251881Speter        SVN_ERR(svn_io_stat(&finfo, text_base_path, APR_FINFO_SIZE, iterpool));
1073251881Speter
1074251881Speter        /* Insert a row into the pristine table. */
1075251881Speter        SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1076251881Speter                                          STMT_INSERT_OR_IGNORE_PRISTINE));
1077251881Speter        SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, iterpool));
1078251881Speter        SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, iterpool));
1079251881Speter        SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
1080251881Speter        SVN_ERR(svn_sqlite__insert(NULL, stmt));
1081251881Speter
1082251881Speter        SVN_ERR(svn_wc__db_pristine_get_future_path(&pristine_path,
1083251881Speter                                                    new_wcroot_abspath,
1084251881Speter                                                    sha1_checksum,
1085251881Speter                                                    iterpool, iterpool));
1086251881Speter
1087251881Speter        /* Ensure any sharding directories exist. */
1088251881Speter        SVN_ERR(svn_wc__ensure_directory(svn_dirent_dirname(pristine_path,
1089251881Speter                                                            iterpool),
1090251881Speter                                         iterpool));
1091251881Speter
1092251881Speter        /* Now move the file into the pristine store, overwriting
1093251881Speter           existing files with the same checksum. */
1094251881Speter        SVN_ERR(svn_io_file_move(temp_path, pristine_path, iterpool));
1095251881Speter      }
1096251881Speter
1097251881Speter      /* Add the checksums for this text-base to *TEXT_BASES_INFO. */
1098251881Speter      {
1099251881Speter        const char *versioned_file_name;
1100251881Speter        svn_boolean_t is_revert_base;
1101251881Speter        svn_wc__text_base_info_t *info;
1102251881Speter        svn_wc__text_base_file_info_t *file_info;
1103251881Speter
1104251881Speter        /* Determine the versioned file name and whether this is a normal base
1105251881Speter         * or a revert base. */
1106251881Speter        versioned_file_name = remove_suffix(text_base_basename,
1107251881Speter                                            SVN_WC__REVERT_EXT, result_pool);
1108251881Speter        if (versioned_file_name)
1109251881Speter          {
1110251881Speter            is_revert_base = TRUE;
1111251881Speter          }
1112251881Speter        else
1113251881Speter          {
1114251881Speter            versioned_file_name = remove_suffix(text_base_basename,
1115251881Speter                                                SVN_WC__BASE_EXT, result_pool);
1116251881Speter            is_revert_base = FALSE;
1117251881Speter          }
1118251881Speter
1119251881Speter        if (! versioned_file_name)
1120251881Speter          {
1121251881Speter             /* Some file that doesn't end with .svn-base or .svn-revert.
1122251881Speter                No idea why that would be in our administrative area, but
1123251881Speter                we shouldn't segfault on this case.
1124251881Speter
1125251881Speter                Note that we already copied this file in the pristine store,
1126251881Speter                but the next cleanup will take care of that.
1127251881Speter              */
1128251881Speter            continue;
1129251881Speter          }
1130251881Speter
1131251881Speter        /* Create a new info struct for this versioned file, or fill in the
1132251881Speter         * existing one if this is the second text-base we've found for it. */
1133251881Speter        info = svn_hash_gets(*text_bases_info, versioned_file_name);
1134251881Speter        if (info == NULL)
1135251881Speter          info = apr_pcalloc(result_pool, sizeof (*info));
1136251881Speter        file_info = (is_revert_base ? &info->revert_base : &info->normal_base);
1137251881Speter
1138251881Speter        file_info->sha1_checksum = svn_checksum_dup(sha1_checksum, result_pool);
1139251881Speter        file_info->md5_checksum = svn_checksum_dup(md5_checksum, result_pool);
1140251881Speter        svn_hash_sets(*text_bases_info, versioned_file_name, info);
1141251881Speter      }
1142251881Speter    }
1143251881Speter
1144251881Speter  svn_pool_destroy(iterpool);
1145251881Speter
1146251881Speter  return SVN_NO_ERROR;
1147251881Speter}
1148251881Speter
1149251881Spetersvn_error_t *
1150251881Spetersvn_wc__upgrade_conflict_skel_from_raw(svn_skel_t **conflicts,
1151251881Speter                                       svn_wc__db_t *db,
1152251881Speter                                       const char *wri_abspath,
1153251881Speter                                       const char *local_relpath,
1154251881Speter                                       const char *conflict_old,
1155251881Speter                                       const char *conflict_wrk,
1156251881Speter                                       const char *conflict_new,
1157251881Speter                                       const char *prej_file,
1158251881Speter                                       const char *tree_conflict_data,
1159251881Speter                                       apr_size_t tree_conflict_len,
1160251881Speter                                       apr_pool_t *result_pool,
1161251881Speter                                       apr_pool_t *scratch_pool)
1162251881Speter{
1163251881Speter  svn_skel_t *conflict_data = NULL;
1164251881Speter  const char *wcroot_abspath;
1165251881Speter
1166251881Speter  SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
1167251881Speter                                scratch_pool, scratch_pool));
1168251881Speter
1169251881Speter  if (conflict_old || conflict_new || conflict_wrk)
1170251881Speter    {
1171251881Speter      const char *old_abspath = NULL;
1172251881Speter      const char *new_abspath = NULL;
1173251881Speter      const char *wrk_abspath = NULL;
1174251881Speter
1175251881Speter      conflict_data = svn_wc__conflict_skel_create(result_pool);
1176251881Speter
1177251881Speter      if (conflict_old)
1178251881Speter        old_abspath = svn_dirent_join(wcroot_abspath, conflict_old,
1179251881Speter                                      scratch_pool);
1180251881Speter
1181251881Speter      if (conflict_new)
1182251881Speter        new_abspath = svn_dirent_join(wcroot_abspath, conflict_new,
1183251881Speter                                      scratch_pool);
1184251881Speter
1185251881Speter      if (conflict_wrk)
1186251881Speter        wrk_abspath = svn_dirent_join(wcroot_abspath, conflict_wrk,
1187251881Speter                                      scratch_pool);
1188251881Speter
1189251881Speter      SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflict_data,
1190251881Speter                                                      db, wri_abspath,
1191251881Speter                                                      wrk_abspath,
1192251881Speter                                                      old_abspath,
1193251881Speter                                                      new_abspath,
1194251881Speter                                                      scratch_pool,
1195251881Speter                                                      scratch_pool));
1196251881Speter    }
1197251881Speter
1198251881Speter  if (prej_file)
1199251881Speter    {
1200251881Speter      const char *prej_abspath;
1201251881Speter
1202251881Speter      if (!conflict_data)
1203251881Speter        conflict_data = svn_wc__conflict_skel_create(result_pool);
1204251881Speter
1205251881Speter      prej_abspath = svn_dirent_join(wcroot_abspath, prej_file, scratch_pool);
1206251881Speter
1207251881Speter      SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflict_data,
1208251881Speter                                                      db, wri_abspath,
1209251881Speter                                                      prej_abspath,
1210251881Speter                                                      NULL, NULL, NULL,
1211251881Speter                                                apr_hash_make(scratch_pool),
1212251881Speter                                                      scratch_pool,
1213251881Speter                                                      scratch_pool));
1214251881Speter    }
1215251881Speter
1216251881Speter  if (tree_conflict_data)
1217251881Speter    {
1218251881Speter      svn_skel_t *tc_skel;
1219251881Speter      const svn_wc_conflict_description2_t *tc;
1220251881Speter      const char *local_abspath;
1221251881Speter
1222251881Speter      if (!conflict_data)
1223251881Speter        conflict_data = svn_wc__conflict_skel_create(scratch_pool);
1224251881Speter
1225251881Speter      tc_skel = svn_skel__parse(tree_conflict_data, tree_conflict_len,
1226251881Speter                                scratch_pool);
1227251881Speter
1228251881Speter      local_abspath = svn_dirent_join(wcroot_abspath, local_relpath,
1229251881Speter                                      scratch_pool);
1230251881Speter
1231251881Speter      SVN_ERR(svn_wc__deserialize_conflict(&tc, tc_skel,
1232251881Speter                                           svn_dirent_dirname(local_abspath,
1233251881Speter                                                              scratch_pool),
1234251881Speter                                           scratch_pool, scratch_pool));
1235251881Speter
1236251881Speter      SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(conflict_data,
1237251881Speter                                                      db, wri_abspath,
1238251881Speter                                                      tc->reason,
1239251881Speter                                                      tc->action,
1240362181Sdim                                                      NULL, NULL,
1241251881Speter                                                      scratch_pool,
1242251881Speter                                                      scratch_pool));
1243251881Speter
1244251881Speter      switch (tc->operation)
1245251881Speter        {
1246251881Speter          case svn_wc_operation_update:
1247251881Speter          default:
1248251881Speter            SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data,
1249251881Speter                                                       tc->src_left_version,
1250251881Speter                                                       tc->src_right_version,
1251251881Speter                                                       scratch_pool,
1252251881Speter                                                       scratch_pool));
1253251881Speter            break;
1254251881Speter          case svn_wc_operation_switch:
1255251881Speter            SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict_data,
1256251881Speter                                                        tc->src_left_version,
1257251881Speter                                                        tc->src_right_version,
1258251881Speter                                                        scratch_pool,
1259251881Speter                                                        scratch_pool));
1260251881Speter            break;
1261251881Speter          case svn_wc_operation_merge:
1262251881Speter            SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_data,
1263251881Speter                                                       tc->src_left_version,
1264251881Speter                                                       tc->src_right_version,
1265251881Speter                                                       scratch_pool,
1266251881Speter                                                       scratch_pool));
1267251881Speter            break;
1268251881Speter        }
1269251881Speter    }
1270251881Speter  else if (conflict_data)
1271251881Speter    {
1272251881Speter      SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data, NULL, NULL,
1273251881Speter                                                  scratch_pool,
1274251881Speter                                                  scratch_pool));
1275251881Speter    }
1276251881Speter
1277251881Speter  *conflicts = conflict_data;
1278251881Speter  return SVN_NO_ERROR;
1279251881Speter}
1280251881Speter
1281251881Speter/* Helper function to upgrade a single conflict from bump_to_30 */
1282251881Speterstatic svn_error_t *
1283251881Speterbump_30_upgrade_one_conflict(svn_wc__db_t *wc_db,
1284251881Speter                             const char *wcroot_abspath,
1285251881Speter                             svn_sqlite__stmt_t *stmt,
1286251881Speter                             svn_sqlite__db_t *sdb,
1287251881Speter                             apr_pool_t *scratch_pool)
1288251881Speter{
1289251881Speter  svn_sqlite__stmt_t *stmt_store;
1290251881Speter  svn_stringbuf_t *skel_data;
1291251881Speter  svn_skel_t *conflict_data;
1292251881Speter  apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0);
1293251881Speter  const char *local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
1294251881Speter  const char *conflict_old = svn_sqlite__column_text(stmt, 2, NULL);
1295251881Speter  const char *conflict_wrk = svn_sqlite__column_text(stmt, 3, NULL);
1296251881Speter  const char *conflict_new = svn_sqlite__column_text(stmt, 4, NULL);
1297251881Speter  const char *prop_reject = svn_sqlite__column_text(stmt, 5, NULL);
1298251881Speter  apr_size_t tree_conflict_size;
1299251881Speter  const char *tree_conflict_data = svn_sqlite__column_blob(stmt, 6,
1300251881Speter                                           &tree_conflict_size, NULL);
1301251881Speter
1302251881Speter  SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(&conflict_data,
1303251881Speter                                                 wc_db, wcroot_abspath,
1304251881Speter                                                 local_relpath,
1305251881Speter                                                 conflict_old,
1306251881Speter                                                 conflict_wrk,
1307251881Speter                                                 conflict_new,
1308251881Speter                                                 prop_reject,
1309251881Speter                                                 tree_conflict_data,
1310251881Speter                                                 tree_conflict_size,
1311251881Speter                                                 scratch_pool, scratch_pool));
1312251881Speter
1313251881Speter  SVN_ERR_ASSERT(conflict_data != NULL);
1314251881Speter
1315251881Speter  skel_data = svn_skel__unparse(conflict_data, scratch_pool);
1316251881Speter
1317251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt_store, sdb,
1318251881Speter                                    STMT_UPGRADE_30_SET_CONFLICT));
1319251881Speter  SVN_ERR(svn_sqlite__bindf(stmt_store, "isb", wc_id, local_relpath,
1320251881Speter                            skel_data->data, skel_data->len));
1321251881Speter  SVN_ERR(svn_sqlite__step_done(stmt_store));
1322251881Speter
1323251881Speter  return SVN_NO_ERROR;
1324251881Speter}
1325251881Speter
1326251881Speterstatic svn_error_t *
1327251881Speterbump_to_30(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
1328251881Speter{
1329251881Speter  struct bump_baton *bb = baton;
1330251881Speter  svn_boolean_t have_row;
1331251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1332251881Speter  svn_sqlite__stmt_t *stmt;
1333251881Speter  svn_wc__db_t *db; /* Read only temp db */
1334251881Speter
1335251881Speter  SVN_ERR(svn_wc__db_open(&db, NULL, TRUE /* open_without_upgrade */, FALSE,
1336251881Speter                          scratch_pool, scratch_pool));
1337251881Speter
1338251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1339251881Speter                                    STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE));
1340251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1341251881Speter
1342251881Speter  while (have_row)
1343251881Speter    {
1344251881Speter      svn_error_t *err;
1345251881Speter      svn_pool_clear(iterpool);
1346251881Speter
1347251881Speter      err = bump_30_upgrade_one_conflict(db, bb->wcroot_abspath, stmt, sdb,
1348251881Speter                                         iterpool);
1349251881Speter
1350251881Speter      if (err)
1351251881Speter        {
1352251881Speter          return svn_error_trace(
1353251881Speter                    svn_error_compose_create(
1354251881Speter                            err,
1355251881Speter                            svn_sqlite__reset(stmt)));
1356251881Speter        }
1357251881Speter
1358251881Speter      SVN_ERR(svn_sqlite__step(&have_row, stmt));
1359251881Speter    }
1360251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1361251881Speter
1362251881Speter  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_30));
1363251881Speter  SVN_ERR(svn_wc__db_close(db));
1364251881Speter  return SVN_NO_ERROR;
1365251881Speter}
1366251881Speter
1367251881Speterstatic svn_error_t *
1368251881Speterbump_to_31(void *baton,
1369251881Speter           svn_sqlite__db_t *sdb,
1370251881Speter           apr_pool_t *scratch_pool)
1371251881Speter{
1372251881Speter  svn_sqlite__stmt_t *stmt, *stmt_mark_switch_roots;
1373251881Speter  svn_boolean_t have_row;
1374251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1375251881Speter  apr_array_header_t *empty_iprops = apr_array_make(
1376251881Speter    scratch_pool, 0, sizeof(svn_prop_inherited_item_t *));
1377251881Speter  svn_error_t *err;
1378251881Speter
1379251881Speter  /* Run additional statements to finalize the upgrade to format 31. */
1380362181Sdim  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_31));
1381251881Speter
1382251881Speter  /* Set inherited_props to an empty array for the roots of all
1383251881Speter     switched subtrees in the WC.  This allows subsequent updates
1384251881Speter     to recognize these roots as needing an iprops cache. */
1385251881Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1386251881Speter                                    STMT_UPGRADE_31_SELECT_WCROOT_NODES));
1387251881Speter  SVN_ERR(svn_sqlite__step(&have_row, stmt));
1388251881Speter
1389251881Speter  err = svn_sqlite__get_statement(&stmt_mark_switch_roots, sdb,
1390251881Speter                                  STMT_UPDATE_IPROP);
1391251881Speter  if (err)
1392251881Speter    return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1393251881Speter
1394251881Speter  while (have_row)
1395251881Speter    {
1396251881Speter      const char *switched_relpath = svn_sqlite__column_text(stmt, 1, NULL);
1397251881Speter      apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0);
1398251881Speter
1399251881Speter      err = svn_sqlite__bindf(stmt_mark_switch_roots, "is", wc_id,
1400251881Speter                              switched_relpath);
1401251881Speter      if (!err)
1402251881Speter        err = svn_sqlite__bind_iprops(stmt_mark_switch_roots, 3,
1403251881Speter                                      empty_iprops, iterpool);
1404251881Speter      if (!err)
1405251881Speter        err = svn_sqlite__step_done(stmt_mark_switch_roots);
1406251881Speter      if (!err)
1407251881Speter        err = svn_sqlite__step(&have_row, stmt);
1408251881Speter
1409251881Speter      if (err)
1410251881Speter        return svn_error_compose_create(
1411251881Speter                err,
1412251881Speter                svn_error_compose_create(
1413251881Speter                  /* Reset in either order is OK. */
1414251881Speter                  svn_sqlite__reset(stmt),
1415251881Speter                  svn_sqlite__reset(stmt_mark_switch_roots)));
1416251881Speter    }
1417251881Speter
1418251881Speter  err = svn_sqlite__reset(stmt_mark_switch_roots);
1419251881Speter  if (err)
1420251881Speter    return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1421251881Speter  SVN_ERR(svn_sqlite__reset(stmt));
1422251881Speter
1423251881Speter  svn_pool_destroy(iterpool);
1424251881Speter
1425251881Speter  return SVN_NO_ERROR;
1426251881Speter}
1427251881Speter
1428289180Speterstatic svn_error_t *
1429289180Speterupgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
1430289180Speter                        const char *dir_relpath,
1431289180Speter                        apr_int64_t wc_id,
1432289180Speter                        apr_hash_t *cache_values,
1433289180Speter                        apr_pool_t *scratch_pool)
1434289180Speter{
1435289180Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1436289180Speter  apr_hash_index_t *hi;
1437289180Speter  svn_sqlite__stmt_t *stmt;
1438251881Speter
1439289180Speter  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
1440289180Speter                                    STMT_UPDATE_BASE_NODE_DAV_CACHE));
1441289180Speter
1442289180Speter  /* Iterate over all the wcprops, writing each one to the wc_db. */
1443289180Speter  for (hi = apr_hash_first(scratch_pool, cache_values);
1444289180Speter       hi;
1445289180Speter       hi = apr_hash_next(hi))
1446289180Speter    {
1447289180Speter      const char *name = apr_hash_this_key(hi);
1448289180Speter      apr_hash_t *props = apr_hash_this_val(hi);
1449289180Speter      const char *local_relpath;
1450289180Speter
1451289180Speter      svn_pool_clear(iterpool);
1452289180Speter
1453289180Speter      local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
1454289180Speter
1455289180Speter      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
1456289180Speter      SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
1457289180Speter      SVN_ERR(svn_sqlite__step_done(stmt));
1458289180Speter    }
1459289180Speter
1460289180Speter  svn_pool_destroy(iterpool);
1461289180Speter
1462289180Speter  return SVN_NO_ERROR;
1463289180Speter}
1464289180Speter
1465289180Speter
1466251881Speterstruct upgrade_data_t {
1467251881Speter  svn_sqlite__db_t *sdb;
1468251881Speter  const char *root_abspath;
1469251881Speter  apr_int64_t repos_id;
1470251881Speter  apr_int64_t wc_id;
1471251881Speter};
1472251881Speter
1473251881Speter/* Upgrade the working copy directory represented by DB/DIR_ABSPATH
1474251881Speter   from OLD_FORMAT to the wc-ng format (SVN_WC__WC_NG_VERSION)'.
1475251881Speter
1476251881Speter   Pass REPOS_INFO_FUNC, REPOS_INFO_BATON and REPOS_CACHE to
1477251881Speter   ensure_repos_info. Add the found repository root and UUID to
1478251881Speter   REPOS_CACHE if it doesn't have a cached entry for this
1479251881Speter   repository.
1480251881Speter
1481251881Speter   *DATA refers to the single root db.
1482251881Speter
1483251881Speter   Uses SCRATCH_POOL for all temporary allocation.  */
1484251881Speterstatic svn_error_t *
1485251881Speterupgrade_to_wcng(void **dir_baton,
1486251881Speter                void *parent_baton,
1487251881Speter                svn_wc__db_t *db,
1488251881Speter                const char *dir_abspath,
1489251881Speter                int old_format,
1490251881Speter                apr_int64_t wc_id,
1491251881Speter                svn_wc_upgrade_get_repos_info_t repos_info_func,
1492251881Speter                void *repos_info_baton,
1493251881Speter                apr_hash_t *repos_cache,
1494251881Speter                const struct upgrade_data_t *data,
1495251881Speter                apr_pool_t *result_pool,
1496251881Speter                apr_pool_t *scratch_pool)
1497251881Speter{
1498251881Speter  const char *logfile_path = svn_wc__adm_child(dir_abspath, ADM_LOG,
1499251881Speter                                               scratch_pool);
1500251881Speter  svn_node_kind_t logfile_on_disk_kind;
1501251881Speter  apr_hash_t *entries;
1502251881Speter  svn_wc_entry_t *this_dir;
1503251881Speter  const char *old_wcroot_abspath, *dir_relpath;
1504251881Speter  apr_hash_t *text_bases_info;
1505251881Speter  svn_error_t *err;
1506251881Speter
1507251881Speter  /* Don't try to mess with the WC if there are old log files left. */
1508251881Speter
1509251881Speter  /* Is the (first) log file present?  */
1510251881Speter  SVN_ERR(svn_io_check_path(logfile_path, &logfile_on_disk_kind,
1511251881Speter                            scratch_pool));
1512251881Speter  if (logfile_on_disk_kind == svn_node_file)
1513251881Speter    return svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
1514251881Speter                            _("Cannot upgrade with existing logs; run a "
1515251881Speter                              "cleanup operation on this working copy using "
1516251881Speter                              "a client version which is compatible with this "
1517251881Speter                              "working copy's format (such as the version "
1518251881Speter                              "you are upgrading from), then retry the "
1519251881Speter                              "upgrade with the current version"));
1520251881Speter
1521251881Speter  /* Lock this working copy directory, or steal an existing lock. Do this
1522251881Speter     BEFORE we read the entries. We don't want another process to modify the
1523251881Speter     entries after we've read them into memory.  */
1524251881Speter  SVN_ERR(create_physical_lock(dir_abspath, scratch_pool));
1525251881Speter
1526251881Speter  /* What's going on here?
1527251881Speter   *
1528251881Speter   * We're attempting to upgrade an older working copy to the new wc-ng format.
1529251881Speter   * The semantics and storage mechanisms between the two are vastly different,
1530251881Speter   * so it's going to be a bit painful.  Here's a plan for the operation:
1531251881Speter   *
1532251881Speter   * 1) Read the old 'entries' using the old-format reader.
1533251881Speter   *
1534251881Speter   * 2) Create the new DB if it hasn't already been created.
1535251881Speter   *
1536251881Speter   * 3) Use our compatibility code for writing entries to fill out the (new)
1537251881Speter   *    DB state.  Use the remembered checksums, since an entry has only the
1538251881Speter   *    MD5 not the SHA1 checksum, and in the case of a revert-base doesn't
1539251881Speter   *    even have that.
1540251881Speter   *
1541251881Speter   * 4) Convert wcprop to the wc-ng format
1542251881Speter   *
1543251881Speter   * 5) Migrate regular properties to the WC-NG DB.
1544251881Speter   */
1545251881Speter
1546251881Speter  /***** ENTRIES - READ *****/
1547251881Speter  SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath,
1548251881Speter                                   scratch_pool, scratch_pool));
1549251881Speter
1550251881Speter  this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
1551251881Speter  SVN_ERR(ensure_repos_info(this_dir, dir_abspath,
1552251881Speter                            repos_info_func, repos_info_baton,
1553251881Speter                            repos_cache,
1554251881Speter                            scratch_pool, scratch_pool));
1555251881Speter
1556251881Speter  /* Cache repos UUID pairs for when a subdir doesn't have this information */
1557251881Speter  if (!svn_hash_gets(repos_cache, this_dir->repos))
1558251881Speter    {
1559251881Speter      apr_pool_t *hash_pool = apr_hash_pool_get(repos_cache);
1560251881Speter
1561251881Speter      svn_hash_sets(repos_cache,
1562251881Speter                    apr_pstrdup(hash_pool, this_dir->repos),
1563251881Speter                    apr_pstrdup(hash_pool, this_dir->uuid));
1564251881Speter    }
1565251881Speter
1566251881Speter  old_wcroot_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
1567251881Speter                                                       data->root_abspath,
1568251881Speter                                                       scratch_pool);
1569251881Speter  dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath, dir_abspath);
1570251881Speter
1571251881Speter  /***** TEXT BASES *****/
1572251881Speter  SVN_ERR(migrate_text_bases(&text_bases_info, dir_abspath, data->root_abspath,
1573251881Speter                             data->sdb, scratch_pool, scratch_pool));
1574251881Speter
1575251881Speter  /***** ENTRIES - WRITE *****/
1576251881Speter  err = svn_wc__write_upgraded_entries(dir_baton, parent_baton, db, data->sdb,
1577251881Speter                                       data->repos_id, data->wc_id,
1578251881Speter                                       dir_abspath, data->root_abspath,
1579251881Speter                                       entries, text_bases_info,
1580251881Speter                                       result_pool, scratch_pool);
1581251881Speter  if (err && err->apr_err == SVN_ERR_WC_CORRUPT)
1582251881Speter    return svn_error_quick_wrap(err,
1583251881Speter                                _("This working copy is corrupt and "
1584251881Speter                                  "cannot be upgraded. Please check out "
1585251881Speter                                  "a new working copy."));
1586251881Speter  else
1587251881Speter    SVN_ERR(err);
1588251881Speter
1589251881Speter  /***** WC PROPS *****/
1590251881Speter  /* If we don't know precisely where the wcprops are, ignore them.  */
1591251881Speter  if (old_format != SVN_WC__WCPROPS_LOST)
1592251881Speter    {
1593251881Speter      apr_hash_t *all_wcprops;
1594251881Speter
1595251881Speter      if (old_format <= SVN_WC__WCPROPS_MANY_FILES_VERSION)
1596251881Speter        SVN_ERR(read_many_wcprops(&all_wcprops, dir_abspath,
1597251881Speter                                  scratch_pool, scratch_pool));
1598251881Speter      else
1599251881Speter        SVN_ERR(read_wcprops(&all_wcprops, dir_abspath,
1600251881Speter                             scratch_pool, scratch_pool));
1601251881Speter
1602289180Speter      SVN_ERR(upgrade_apply_dav_cache(data->sdb, dir_relpath, wc_id,
1603289180Speter                                      all_wcprops, scratch_pool));
1604251881Speter    }
1605251881Speter
1606251881Speter  /* Upgrade all the properties (including "this dir").
1607251881Speter
1608251881Speter     Note: this must come AFTER the entries have been migrated into the
1609251881Speter     database. The upgrade process needs the children in BASE_NODE and
1610251881Speter     WORKING_NODE, and to examine the resultant WORKING state.  */
1611251881Speter  SVN_ERR(migrate_props(dir_abspath, data->root_abspath, data->sdb, old_format,
1612251881Speter                        wc_id, scratch_pool));
1613251881Speter
1614251881Speter  return SVN_NO_ERROR;
1615251881Speter}
1616251881Speter
1617251881Speterconst char *
1618251881Spetersvn_wc__version_string_from_format(int wc_format)
1619251881Speter{
1620251881Speter  switch (wc_format)
1621251881Speter    {
1622251881Speter      case 4: return "<=1.3";
1623251881Speter      case 8: return "1.4";
1624251881Speter      case 9: return "1.5";
1625251881Speter      case 10: return "1.6";
1626251881Speter      case SVN_WC__WC_NG_VERSION: return "1.7";
1627251881Speter    }
1628251881Speter  return _("(unreleased development version)");
1629251881Speter}
1630251881Speter
1631251881Spetersvn_error_t *
1632251881Spetersvn_wc__upgrade_sdb(int *result_format,
1633251881Speter                    const char *wcroot_abspath,
1634251881Speter                    svn_sqlite__db_t *sdb,
1635251881Speter                    int start_format,
1636251881Speter                    apr_pool_t *scratch_pool)
1637251881Speter{
1638251881Speter  struct bump_baton bb;
1639251881Speter
1640251881Speter  bb.wcroot_abspath = wcroot_abspath;
1641251881Speter
1642251881Speter  if (start_format < SVN_WC__WC_NG_VERSION /* 12 */)
1643251881Speter    return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
1644251881Speter                             _("Working copy '%s' is too old (format %d, "
1645251881Speter                               "created by Subversion %s)"),
1646251881Speter                             svn_dirent_local_style(wcroot_abspath,
1647251881Speter                                                    scratch_pool),
1648251881Speter                             start_format,
1649251881Speter                             svn_wc__version_string_from_format(start_format));
1650251881Speter
1651251881Speter  /* Early WCNG formats no longer supported. */
1652251881Speter  if (start_format < 19)
1653251881Speter    return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
1654251881Speter                             _("Working copy '%s' is an old development "
1655251881Speter                               "version (format %d); to upgrade it, "
1656251881Speter                               "use a format 18 client, then "
1657251881Speter                               "use 'tools/dev/wc-ng/bump-to-19.py', then "
1658251881Speter                               "use the current client"),
1659251881Speter                             svn_dirent_local_style(wcroot_abspath,
1660251881Speter                                                    scratch_pool),
1661251881Speter                             start_format);
1662362181Sdim  else if (start_format < 29)
1663362181Sdim    return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
1664362181Sdim                             _("Working copy '%s' is an old development "
1665362181Sdim                               "version (format %d); to upgrade it, "
1666362181Sdim                               "use a Subversion 1.7-1.9 client, then "
1667362181Sdim                               "use the current client"),
1668362181Sdim                             svn_dirent_local_style(wcroot_abspath,
1669362181Sdim                                                    scratch_pool),
1670362181Sdim                             start_format);
1671251881Speter
1672251881Speter  /* ### need lock-out. only one upgrade at a time. note that other code
1673251881Speter     ### cannot use this un-upgraded database until we finish the upgrade.  */
1674251881Speter
1675251881Speter  /* Note: none of these have "break" statements; the fall-through is
1676251881Speter     intentional. */
1677251881Speter  switch (start_format)
1678251881Speter    {
1679251881Speter      case 29:
1680251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_30, &bb,
1681251881Speter                                             scratch_pool));
1682251881Speter        *result_format = 30;
1683251881Speter
1684251881Speter      case 30:
1685251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_31, &bb,
1686251881Speter                                             scratch_pool));
1687251881Speter        *result_format = 31;
1688251881Speter        /* FALLTHROUGH  */
1689251881Speter      /* ### future bumps go here.  */
1690251881Speter#if 0
1691251881Speter      case XXX-1:
1692251881Speter        /* Revamp the recording of tree conflicts.  */
1693251881Speter        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_XXX, &bb,
1694251881Speter                                             scratch_pool));
1695251881Speter        *result_format = XXX;
1696251881Speter        /* FALLTHROUGH  */
1697251881Speter#endif
1698251881Speter      case SVN_WC__VERSION:
1699251881Speter        /* already upgraded */
1700251881Speter        *result_format = SVN_WC__VERSION;
1701262250Speter
1702262250Speter        SVN_SQLITE__WITH_LOCK(
1703262250Speter            svn_wc__db_install_schema_statistics(sdb, scratch_pool),
1704262250Speter            sdb);
1705251881Speter    }
1706251881Speter
1707251881Speter#ifdef SVN_DEBUG
1708251881Speter  if (*result_format != start_format)
1709251881Speter    {
1710251881Speter      int schema_version;
1711251881Speter      SVN_ERR(svn_sqlite__read_schema_version(&schema_version, sdb, scratch_pool));
1712251881Speter
1713251881Speter      /* If this assertion fails the schema isn't updated correctly */
1714251881Speter      SVN_ERR_ASSERT(schema_version == *result_format);
1715251881Speter    }
1716251881Speter#endif
1717251881Speter
1718251881Speter  /* Zap anything that might be remaining or escaped our notice.  */
1719251881Speter  wipe_obsolete_files(wcroot_abspath, scratch_pool);
1720251881Speter
1721251881Speter  return SVN_NO_ERROR;
1722251881Speter}
1723251881Speter
1724251881Speter
1725251881Speter/* */
1726251881Speterstatic svn_error_t *
1727251881Speterupgrade_working_copy(void *parent_baton,
1728251881Speter                     svn_wc__db_t *db,
1729251881Speter                     const char *dir_abspath,
1730251881Speter                     svn_wc_upgrade_get_repos_info_t repos_info_func,
1731251881Speter                     void *repos_info_baton,
1732251881Speter                     apr_hash_t *repos_cache,
1733251881Speter                     const struct upgrade_data_t *data,
1734251881Speter                     svn_cancel_func_t cancel_func,
1735251881Speter                     void *cancel_baton,
1736251881Speter                     svn_wc_notify_func2_t notify_func,
1737251881Speter                     void *notify_baton,
1738251881Speter                     apr_pool_t *result_pool,
1739251881Speter                     apr_pool_t *scratch_pool)
1740251881Speter{
1741251881Speter  void *dir_baton;
1742251881Speter  int old_format;
1743251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1744251881Speter  apr_array_header_t *subdirs;
1745251881Speter  svn_error_t *err;
1746251881Speter  int i;
1747251881Speter
1748251881Speter  if (cancel_func)
1749251881Speter    SVN_ERR(cancel_func(cancel_baton));
1750251881Speter
1751251881Speter  SVN_ERR(svn_wc__db_temp_get_format(&old_format, db, dir_abspath,
1752251881Speter                                     iterpool));
1753251881Speter
1754251881Speter  if (old_format >= SVN_WC__WC_NG_VERSION)
1755251881Speter    {
1756251881Speter      if (notify_func)
1757251881Speter        notify_func(notify_baton,
1758251881Speter                    svn_wc_create_notify(dir_abspath, svn_wc_notify_skip,
1759251881Speter                                         iterpool),
1760251881Speter                iterpool);
1761251881Speter      svn_pool_destroy(iterpool);
1762251881Speter      return SVN_NO_ERROR;
1763251881Speter    }
1764251881Speter
1765251881Speter  err = get_versioned_subdirs(&subdirs, NULL, dir_abspath, FALSE,
1766251881Speter                              scratch_pool, iterpool);
1767251881Speter  if (err)
1768251881Speter    {
1769251881Speter      if (APR_STATUS_IS_ENOENT(err->apr_err)
1770251881Speter          || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))
1771251881Speter        {
1772251881Speter          /* An unversioned dir is obstructing a versioned dir */
1773251881Speter          svn_error_clear(err);
1774251881Speter          err = NULL;
1775251881Speter          if (notify_func)
1776251881Speter            notify_func(notify_baton,
1777251881Speter                        svn_wc_create_notify(dir_abspath, svn_wc_notify_skip,
1778251881Speter                                             iterpool),
1779251881Speter                        iterpool);
1780251881Speter        }
1781251881Speter      svn_pool_destroy(iterpool);
1782251881Speter      return err;
1783251881Speter    }
1784251881Speter
1785251881Speter
1786251881Speter  SVN_ERR(upgrade_to_wcng(&dir_baton, parent_baton, db, dir_abspath,
1787251881Speter                          old_format, data->wc_id,
1788251881Speter                          repos_info_func, repos_info_baton,
1789251881Speter                          repos_cache, data, scratch_pool, iterpool));
1790251881Speter
1791251881Speter  if (notify_func)
1792251881Speter    notify_func(notify_baton,
1793251881Speter                svn_wc_create_notify(dir_abspath, svn_wc_notify_upgraded_path,
1794251881Speter                                     iterpool),
1795251881Speter                iterpool);
1796251881Speter
1797251881Speter  for (i = 0; i < subdirs->nelts; ++i)
1798251881Speter    {
1799251881Speter      const char *child_abspath = APR_ARRAY_IDX(subdirs, i, const char *);
1800251881Speter
1801251881Speter      svn_pool_clear(iterpool);
1802251881Speter
1803251881Speter      SVN_ERR(upgrade_working_copy(dir_baton, db, child_abspath,
1804251881Speter                                   repos_info_func, repos_info_baton,
1805251881Speter                                   repos_cache, data,
1806251881Speter                                   cancel_func, cancel_baton,
1807251881Speter                                   notify_func, notify_baton,
1808251881Speter                                   iterpool, iterpool));
1809251881Speter    }
1810251881Speter
1811251881Speter  svn_pool_destroy(iterpool);
1812251881Speter
1813251881Speter  return SVN_NO_ERROR;
1814251881Speter}
1815251881Speter
1816251881Speter
1817251881Speter/* Return a verbose error if LOCAL_ABSPATH is a not a pre-1.7 working
1818251881Speter   copy root */
1819251881Speterstatic svn_error_t *
1820251881Speteris_old_wcroot(const char *local_abspath,
1821251881Speter              apr_pool_t *scratch_pool)
1822251881Speter{
1823251881Speter  apr_hash_t *entries;
1824251881Speter  const char *parent_abspath, *name;
1825251881Speter  svn_wc_entry_t *entry;
1826251881Speter  svn_error_t *err = svn_wc__read_entries_old(&entries, local_abspath,
1827251881Speter                                              scratch_pool, scratch_pool);
1828251881Speter  if (err)
1829251881Speter    {
1830251881Speter      return svn_error_createf(
1831251881Speter        SVN_ERR_WC_INVALID_OP_ON_CWD, err,
1832251881Speter        _("Can't upgrade '%s' as it is not a working copy"),
1833251881Speter        svn_dirent_local_style(local_abspath, scratch_pool));
1834251881Speter    }
1835251881Speter  else if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
1836251881Speter    return SVN_NO_ERROR;
1837251881Speter
1838251881Speter  svn_dirent_split(&parent_abspath, &name, local_abspath, scratch_pool);
1839251881Speter
1840251881Speter  err = svn_wc__read_entries_old(&entries, parent_abspath,
1841251881Speter                                 scratch_pool, scratch_pool);
1842251881Speter  if (err)
1843251881Speter    {
1844251881Speter      svn_error_clear(err);
1845251881Speter      return SVN_NO_ERROR;
1846251881Speter    }
1847251881Speter
1848251881Speter  entry = svn_hash_gets(entries, name);
1849251881Speter  if (!entry
1850251881Speter      || entry->absent
1851251881Speter      || (entry->deleted && entry->schedule != svn_wc_schedule_add)
1852251881Speter      || entry->depth == svn_depth_exclude)
1853251881Speter    {
1854251881Speter      return SVN_NO_ERROR;
1855251881Speter    }
1856251881Speter
1857251881Speter  while (!svn_dirent_is_root(parent_abspath, strlen(parent_abspath)))
1858251881Speter    {
1859251881Speter      svn_dirent_split(&parent_abspath, &name, parent_abspath, scratch_pool);
1860251881Speter      err = svn_wc__read_entries_old(&entries, parent_abspath,
1861251881Speter                                     scratch_pool, scratch_pool);
1862251881Speter      if (err)
1863251881Speter        {
1864251881Speter          svn_error_clear(err);
1865251881Speter          parent_abspath = svn_dirent_join(parent_abspath, name, scratch_pool);
1866251881Speter          break;
1867251881Speter        }
1868251881Speter      entry = svn_hash_gets(entries, name);
1869251881Speter      if (!entry
1870251881Speter          || entry->absent
1871251881Speter          || (entry->deleted && entry->schedule != svn_wc_schedule_add)
1872251881Speter          || entry->depth == svn_depth_exclude)
1873251881Speter        {
1874251881Speter          parent_abspath = svn_dirent_join(parent_abspath, name, scratch_pool);
1875251881Speter          break;
1876251881Speter        }
1877251881Speter    }
1878251881Speter
1879251881Speter  return svn_error_createf(
1880251881Speter    SVN_ERR_WC_INVALID_OP_ON_CWD, NULL,
1881251881Speter    _("Can't upgrade '%s' as it is not a working copy root,"
1882251881Speter      " the root is '%s'"),
1883251881Speter    svn_dirent_local_style(local_abspath, scratch_pool),
1884251881Speter    svn_dirent_local_style(parent_abspath, scratch_pool));
1885251881Speter}
1886251881Speter
1887251881Spetersvn_error_t *
1888251881Spetersvn_wc_upgrade(svn_wc_context_t *wc_ctx,
1889251881Speter               const char *local_abspath,
1890251881Speter               svn_wc_upgrade_get_repos_info_t repos_info_func,
1891251881Speter               void *repos_info_baton,
1892251881Speter               svn_cancel_func_t cancel_func,
1893251881Speter               void *cancel_baton,
1894251881Speter               svn_wc_notify_func2_t notify_func,
1895251881Speter               void *notify_baton,
1896251881Speter               apr_pool_t *scratch_pool)
1897251881Speter{
1898251881Speter  svn_wc__db_t *db;
1899251881Speter  struct upgrade_data_t data = { NULL };
1900251881Speter  svn_skel_t *work_item, *work_items = NULL;
1901251881Speter  const char *pristine_from, *pristine_to, *db_from, *db_to;
1902251881Speter  apr_hash_t *repos_cache = apr_hash_make(scratch_pool);
1903251881Speter  svn_wc_entry_t *this_dir;
1904251881Speter  apr_hash_t *entries;
1905251881Speter  const char *root_adm_abspath;
1906251881Speter  svn_error_t *err;
1907251881Speter  int result_format;
1908253734Speter  svn_boolean_t bumped_format;
1909251881Speter
1910251881Speter  /* Try upgrading a wc-ng-style working copy. */
1911251881Speter  SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, TRUE, FALSE,
1912251881Speter                          scratch_pool, scratch_pool));
1913251881Speter
1914251881Speter
1915253734Speter  err = svn_wc__db_bump_format(&result_format, &bumped_format,
1916253734Speter                               db, local_abspath,
1917251881Speter                               scratch_pool);
1918251881Speter  if (err)
1919251881Speter    {
1920251881Speter      if (err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
1921251881Speter        {
1922251881Speter          return svn_error_trace(
1923251881Speter                    svn_error_compose_create(
1924251881Speter                            err,
1925251881Speter                            svn_wc__db_close(db)));
1926251881Speter        }
1927251881Speter
1928251881Speter      svn_error_clear(err);
1929251881Speter      /* Pre 1.7: Fall through */
1930251881Speter    }
1931251881Speter  else
1932251881Speter    {
1933251881Speter      /* Auto-upgrade worked! */
1934251881Speter      SVN_ERR(svn_wc__db_close(db));
1935251881Speter
1936251881Speter      SVN_ERR_ASSERT(result_format == SVN_WC__VERSION);
1937251881Speter
1938253734Speter      if (bumped_format && notify_func)
1939253734Speter        {
1940253734Speter          svn_wc_notify_t *notify;
1941253734Speter
1942253734Speter          notify = svn_wc_create_notify(local_abspath,
1943253734Speter                                        svn_wc_notify_upgraded_path,
1944253734Speter                                        scratch_pool);
1945253734Speter
1946253734Speter          notify_func(notify_baton, notify, scratch_pool);
1947253734Speter        }
1948253734Speter
1949251881Speter      return SVN_NO_ERROR;
1950251881Speter    }
1951251881Speter
1952251881Speter  SVN_ERR(is_old_wcroot(local_abspath, scratch_pool));
1953251881Speter
1954251881Speter  /* Given a pre-wcng root some/wc we create a temporary wcng in
1955251881Speter     some/wc/.svn/tmp/wcng/wc.db and copy the metadata from one to the
1956251881Speter     other, then the temporary wc.db file gets moved into the original
1957251881Speter     root.  Until the wc.db file is moved the original working copy
1958251881Speter     remains a pre-wcng and 'cleanup' with an old client will remove
1959251881Speter     the partial upgrade.  Moving the wc.db file creates a wcng, and
1960251881Speter     'cleanup' with a new client will complete any outstanding
1961251881Speter     upgrade. */
1962251881Speter
1963251881Speter  SVN_ERR(svn_wc__read_entries_old(&entries, local_abspath,
1964251881Speter                                   scratch_pool, scratch_pool));
1965251881Speter
1966251881Speter  this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
1967251881Speter  SVN_ERR(ensure_repos_info(this_dir, local_abspath, repos_info_func,
1968251881Speter                            repos_info_baton, repos_cache,
1969251881Speter                            scratch_pool, scratch_pool));
1970251881Speter
1971251881Speter  /* Cache repos UUID pairs for when a subdir doesn't have this information */
1972251881Speter  if (!svn_hash_gets(repos_cache, this_dir->repos))
1973251881Speter    svn_hash_sets(repos_cache,
1974251881Speter                  apr_pstrdup(scratch_pool, this_dir->repos),
1975251881Speter                  apr_pstrdup(scratch_pool, this_dir->uuid));
1976251881Speter
1977251881Speter  /* Create the new DB in the temporary root wc/.svn/tmp/wcng/.svn */
1978251881Speter  data.root_abspath = svn_dirent_join(svn_wc__adm_child(local_abspath, "tmp",
1979251881Speter                                                        scratch_pool),
1980251881Speter                                       "wcng", scratch_pool);
1981251881Speter  root_adm_abspath = svn_wc__adm_child(data.root_abspath, "",
1982251881Speter                                       scratch_pool);
1983251881Speter  SVN_ERR(svn_io_remove_dir2(root_adm_abspath, TRUE, NULL, NULL,
1984251881Speter                             scratch_pool));
1985251881Speter  SVN_ERR(svn_wc__ensure_directory(root_adm_abspath, scratch_pool));
1986251881Speter
1987251881Speter  /* Create an empty sqlite database for this directory and store it in DB. */
1988251881Speter  SVN_ERR(svn_wc__db_upgrade_begin(&data.sdb,
1989251881Speter                                   &data.repos_id, &data.wc_id,
1990251881Speter                                   db, data.root_abspath,
1991251881Speter                                   this_dir->repos, this_dir->uuid,
1992251881Speter                                   scratch_pool));
1993251881Speter
1994251881Speter  /* Migrate the entries over to the new database.
1995251881Speter   ### We need to think about atomicity here.
1996251881Speter
1997251881Speter   entries_write_new() writes in current format rather than
1998251881Speter   f12. Thus, this function bumps a working copy all the way to
1999251881Speter   current.  */
2000251881Speter  SVN_ERR(svn_wc__db_wclock_obtain(db, data.root_abspath, 0, FALSE,
2001251881Speter                                   scratch_pool));
2002251881Speter
2003289180Speter  SVN_SQLITE__WITH_LOCK(
2004289180Speter    upgrade_working_copy(NULL, db, local_abspath,
2005289180Speter                         repos_info_func, repos_info_baton,
2006289180Speter                         repos_cache, &data,
2007289180Speter                         cancel_func, cancel_baton,
2008289180Speter                         notify_func, notify_baton,
2009289180Speter                         scratch_pool, scratch_pool),
2010289180Speter    data.sdb);
2011251881Speter
2012251881Speter  /* A workqueue item to move the pristine dir into place */
2013251881Speter  pristine_from = svn_wc__adm_child(data.root_abspath, PRISTINE_STORAGE_RELPATH,
2014251881Speter                                    scratch_pool);
2015251881Speter  pristine_to = svn_wc__adm_child(local_abspath, PRISTINE_STORAGE_RELPATH,
2016251881Speter                                  scratch_pool);
2017251881Speter  SVN_ERR(svn_wc__ensure_directory(pristine_from, scratch_pool));
2018251881Speter  SVN_ERR(svn_wc__wq_build_file_move(&work_item, db, local_abspath,
2019251881Speter                                     pristine_from, pristine_to,
2020251881Speter                                     scratch_pool, scratch_pool));
2021251881Speter  work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2022251881Speter
2023251881Speter  /* A workqueue item to remove pre-wcng metadata */
2024251881Speter  SVN_ERR(svn_wc__wq_build_postupgrade(&work_item, scratch_pool));
2025251881Speter  work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2026251881Speter  SVN_ERR(svn_wc__db_wq_add(db, data.root_abspath, work_items, scratch_pool));
2027251881Speter
2028251881Speter  SVN_ERR(svn_wc__db_wclock_release(db, data.root_abspath, scratch_pool));
2029251881Speter  SVN_ERR(svn_wc__db_close(db));
2030251881Speter
2031251881Speter  /* Renaming the db file is what makes the pre-wcng into a wcng */
2032251881Speter  db_from = svn_wc__adm_child(data.root_abspath, SDB_FILE, scratch_pool);
2033251881Speter  db_to = svn_wc__adm_child(local_abspath, SDB_FILE, scratch_pool);
2034362181Sdim  SVN_ERR(svn_io_file_rename2(db_from, db_to, FALSE, scratch_pool));
2035251881Speter
2036251881Speter  /* Now we have a working wcng, tidy up the droppings */
2037251881Speter  SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, FALSE, FALSE,
2038251881Speter                          scratch_pool, scratch_pool));
2039251881Speter  SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2040251881Speter                         scratch_pool));
2041251881Speter  SVN_ERR(svn_wc__db_close(db));
2042251881Speter
2043251881Speter  /* Should we have the workqueue remove this empty dir? */
2044251881Speter  SVN_ERR(svn_io_remove_dir2(data.root_abspath, FALSE, NULL, NULL,
2045251881Speter                             scratch_pool));
2046251881Speter
2047251881Speter  return SVN_NO_ERROR;
2048251881Speter}
2049251881Speter
2050251881Spetersvn_error_t *
2051251881Spetersvn_wc__upgrade_add_external_info(svn_wc_context_t *wc_ctx,
2052251881Speter                                  const char *local_abspath,
2053251881Speter                                  svn_node_kind_t kind,
2054251881Speter                                  const char *def_local_abspath,
2055251881Speter                                  const char *repos_relpath,
2056251881Speter                                  const char *repos_root_url,
2057251881Speter                                  const char *repos_uuid,
2058251881Speter                                  svn_revnum_t def_peg_revision,
2059251881Speter                                  svn_revnum_t def_revision,
2060251881Speter                                  apr_pool_t *scratch_pool)
2061251881Speter{
2062251881Speter  svn_node_kind_t db_kind;
2063251881Speter  switch (kind)
2064251881Speter    {
2065251881Speter      case svn_node_dir:
2066251881Speter        db_kind = svn_node_dir;
2067251881Speter        break;
2068251881Speter
2069251881Speter      case svn_node_file:
2070251881Speter        db_kind = svn_node_file;
2071251881Speter        break;
2072251881Speter
2073251881Speter      case svn_node_unknown:
2074251881Speter        db_kind = svn_node_unknown;
2075251881Speter        break;
2076251881Speter
2077251881Speter      default:
2078251881Speter        SVN_ERR_MALFUNCTION();
2079251881Speter    }
2080251881Speter
2081251881Speter  SVN_ERR(svn_wc__db_upgrade_insert_external(wc_ctx->db, local_abspath,
2082251881Speter                                             db_kind,
2083251881Speter                                             svn_dirent_dirname(local_abspath,
2084251881Speter                                                                scratch_pool),
2085251881Speter                                             def_local_abspath, repos_relpath,
2086251881Speter                                             repos_root_url, repos_uuid,
2087251881Speter                                             def_peg_revision, def_revision,
2088251881Speter                                             scratch_pool));
2089251881Speter  return SVN_NO_ERROR;
2090251881Speter}
2091