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