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