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