]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_wc/wc_db_wcroot.c
Update Subversion and dependencies to 1.14.0 LTS.
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / libsvn_wc / wc_db_wcroot.c
1 /*
2  * wc_db_wcroot.c :  supporting datastructures for the administrative database
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 #define SVN_WC__I_AM_WC_DB
25
26 #include <assert.h>
27
28 #include "svn_dirent_uri.h"
29 #include "svn_hash.h"
30 #include "svn_path.h"
31 #include "svn_pools.h"
32 #include "svn_version.h"
33
34 #include "wc.h"
35 #include "adm_files.h"
36 #include "wc_db_private.h"
37 #include "wc-queries.h"
38
39 #include "svn_private_config.h"
40
41 /* ### Same values as wc_db.c */
42 #define SDB_FILE  "wc.db"
43 #define UNKNOWN_WC_ID ((apr_int64_t) -1)
44 #define FORMAT_FROM_SDB (-1)
45
46 /* #define VERIFY_ON_CLOSE */
47 \f
48 /* Get the format version from a wc-1 directory. If it is not a working copy
49    directory, then it sets VERSION to zero and returns no error.  */
50 static svn_error_t *
51 get_old_version(int *version,
52                 const char *abspath,
53                 apr_pool_t *scratch_pool)
54 {
55   svn_error_t *err;
56   const char *format_file_path;
57   svn_node_kind_t kind;
58
59   /* Try reading the format number from the entries file.  */
60   format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_ENTRIES,
61                                        scratch_pool);
62
63   /* Since trying to open a non-existent file is quite expensive, try a
64      quick stat call first. In wc-ng w/cs, this will be an early exit. */
65   SVN_ERR(svn_io_check_path(format_file_path, &kind, scratch_pool));
66   if (kind == svn_node_none)
67     {
68       *version = 0;
69       return SVN_NO_ERROR;
70     }
71
72   err = svn_io_read_version_file(version, format_file_path, scratch_pool);
73   if (err == NULL)
74     return SVN_NO_ERROR;
75   if (err->apr_err != SVN_ERR_BAD_VERSION_FILE_FORMAT
76       && !APR_STATUS_IS_ENOENT(err->apr_err)
77       && !APR_STATUS_IS_ENOTDIR(err->apr_err))
78     return svn_error_createf(SVN_ERR_WC_MISSING, err, _("'%s' does not exist"),
79                              svn_dirent_local_style(abspath, scratch_pool));
80   svn_error_clear(err);
81
82   /* This must be a really old working copy!  Fall back to reading the
83      format file.
84
85      Note that the format file might not exist in newer working copies
86      (format 7 and higher), but in that case, the entries file should
87      have contained the format number. */
88   format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_FORMAT,
89                                        scratch_pool);
90   err = svn_io_read_version_file(version, format_file_path, scratch_pool);
91   if (err == NULL)
92     return SVN_NO_ERROR;
93
94   /* Whatever error may have occurred... we can just ignore. This is not
95      a working copy directory. Signal the caller.  */
96   svn_error_clear(err);
97
98   *version = 0;
99   return SVN_NO_ERROR;
100 }
101
102
103 /* A helper function to parse_local_abspath() which returns the on-disk KIND
104    of LOCAL_ABSPATH, using DB and SCRATCH_POOL as needed.
105
106    This function may do strange things, but at long as it comes up with the
107    Right Answer, we should be happy. */
108 static svn_error_t *
109 get_path_kind(svn_node_kind_t *kind,
110               svn_wc__db_t *db,
111               const char *local_abspath,
112               apr_pool_t *scratch_pool)
113 {
114   svn_boolean_t special;
115   svn_node_kind_t node_kind;
116
117   /* This implements a *really* simple LRU cache, where "simple" is defined
118      as "only one element".  In other words, we remember the most recently
119      queried path, and nothing else.  This gives >80% cache hits. */
120
121   if (db->parse_cache.abspath
122         && strcmp(db->parse_cache.abspath->data, local_abspath) == 0)
123     {
124       /* Cache hit! */
125       *kind = db->parse_cache.kind;
126       return SVN_NO_ERROR;
127     }
128
129   if (!db->parse_cache.abspath)
130     {
131       db->parse_cache.abspath = svn_stringbuf_create(local_abspath,
132                                                      db->state_pool);
133     }
134   else
135     {
136       svn_stringbuf_set(db->parse_cache.abspath, local_abspath);
137     }
138
139   SVN_ERR(svn_io_check_special_path(local_abspath, &node_kind,
140                                     &special, scratch_pool));
141
142   db->parse_cache.kind = (special ? svn_node_symlink : node_kind);
143   *kind = db->parse_cache.kind;
144
145   return SVN_NO_ERROR;
146 }
147
148
149 svn_error_t *
150 svn_wc__db_verify_no_work(svn_sqlite__db_t *sdb)
151 {
152   svn_sqlite__stmt_t *stmt;
153   svn_boolean_t have_row;
154
155   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_LOOK_FOR_WORK));
156   SVN_ERR(svn_sqlite__step(&have_row, stmt));
157   SVN_ERR(svn_sqlite__reset(stmt));
158
159   if (have_row)
160     return svn_error_create(SVN_ERR_WC_CLEANUP_REQUIRED, NULL,
161                             NULL /* nothing to add.  */);
162
163   return SVN_NO_ERROR;
164 }
165
166 #if defined(VERIFY_ON_CLOSE) && defined(SVN_DEBUG)
167 /* Implements svn_wc__db_verify_cb_t */
168 static svn_error_t *
169 verify_db_cb(void *baton,
170              const char *wc_abspath,
171              const char *local_relpath,
172              int op_depth,
173              int id,
174              const char *msg,
175              apr_pool_t *scratch_pool)
176 {
177   if (op_depth >= 0)
178     SVN_DBG(("DB-VRFY: %s: %s (%d): SV%04d %s",
179               wc_abspath, local_relpath, op_depth, id, msg));
180   else
181     SVN_DBG(("DB-VRFY: %s: %s: SV%04d %s",
182               wc_abspath, local_relpath, id, msg));
183
184   return SVN_NO_ERROR;
185 }
186 #endif
187
188 /* */
189 static apr_status_t
190 close_wcroot(void *data)
191 {
192   svn_wc__db_wcroot_t *wcroot = data;
193   svn_error_t *err;
194
195   SVN_ERR_ASSERT_NO_RETURN(wcroot->sdb != NULL);
196
197 #if defined(VERIFY_ON_CLOSE) && defined(SVN_DEBUG)
198   if (getenv("SVN_CMDLINE_VERIFY_SQL_AT_CLOSE"))
199     {
200       apr_pool_t *scratch_pool = svn_pool_create(NULL);
201
202       svn_error_clear(svn_wc__db_verify_db_full_internal(
203                                     wcroot, verify_db_cb, NULL, scratch_pool));
204
205       svn_pool_destroy(scratch_pool);
206     }
207 #endif
208
209   err = svn_sqlite__close(wcroot->sdb);
210   wcroot->sdb = NULL;
211   if (err)
212     {
213       apr_status_t result = err->apr_err;
214       svn_error_clear(err);
215       return result;
216     }
217
218   return APR_SUCCESS;
219 }
220
221
222 svn_error_t *
223 svn_wc__db_open(svn_wc__db_t **db,
224                 svn_config_t *config,
225                 svn_boolean_t open_without_upgrade,
226                 svn_boolean_t enforce_empty_wq,
227                 apr_pool_t *result_pool,
228                 apr_pool_t *scratch_pool)
229 {
230   *db = apr_pcalloc(result_pool, sizeof(**db));
231   (*db)->config = config;
232   (*db)->verify_format = !open_without_upgrade;
233   (*db)->enforce_empty_wq = enforce_empty_wq;
234   (*db)->dir_data = apr_hash_make(result_pool);
235
236   (*db)->state_pool = result_pool;
237
238   /* Don't need to initialize (*db)->parse_cache, due to the calloc above */
239   if (config)
240     {
241       svn_error_t *err;
242       svn_boolean_t sqlite_exclusive = FALSE;
243       apr_int64_t timeout;
244
245       err = svn_config_get_bool(config, &sqlite_exclusive,
246                                 SVN_CONFIG_SECTION_WORKING_COPY,
247                                 SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
248                                 FALSE);
249       if (err)
250         {
251           svn_error_clear(err);
252         }
253       else
254         (*db)->exclusive = sqlite_exclusive;
255
256       err = svn_config_get_int64(config, &timeout,
257                                  SVN_CONFIG_SECTION_WORKING_COPY,
258                                  SVN_CONFIG_OPTION_SQLITE_BUSY_TIMEOUT,
259                                  0);
260       if (err || timeout < 0 || timeout > APR_INT32_MAX)
261         svn_error_clear(err);
262       else
263         (*db)->timeout = (apr_int32_t)timeout;
264     }
265
266   return SVN_NO_ERROR;
267 }
268
269
270 svn_error_t *
271 svn_wc__db_close(svn_wc__db_t *db)
272 {
273   apr_pool_t *scratch_pool = db->state_pool;
274   apr_hash_t *roots = apr_hash_make(scratch_pool);
275   apr_hash_index_t *hi;
276
277   /* Collect all the unique WCROOT structures, and empty out DIR_DATA.  */
278   for (hi = apr_hash_first(scratch_pool, db->dir_data);
279        hi;
280        hi = apr_hash_next(hi))
281     {
282       svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi);
283       const char *local_abspath = apr_hash_this_key(hi);
284
285       if (wcroot->sdb)
286         svn_hash_sets(roots, wcroot->abspath, wcroot);
287
288       svn_hash_sets(db->dir_data, local_abspath, NULL);
289     }
290
291   /* Run the cleanup for each WCROOT.  */
292   return svn_error_trace(svn_wc__db_close_many_wcroots(roots, db->state_pool,
293                                                        scratch_pool));
294 }
295
296
297 svn_error_t *
298 svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot,
299                              const char *wcroot_abspath,
300                              svn_sqlite__db_t *sdb,
301                              apr_int64_t wc_id,
302                              int format,
303                              svn_boolean_t verify_format,
304                              apr_pool_t *result_pool,
305                              apr_pool_t *scratch_pool)
306 {
307   if (sdb && format == FORMAT_FROM_SDB)
308     SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
309
310   /* If we construct a wcroot, then we better have a format.  */
311   SVN_ERR_ASSERT(format >= 1);
312
313   /* If this working copy is PRE-1.0, then simply bail out.  */
314   if (format < 4)
315     {
316       return svn_error_createf(
317         SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
318         _("Working copy format of '%s' is too old (%d); "
319           "please check out your working copy again"),
320         svn_dirent_local_style(wcroot_abspath, scratch_pool), format);
321     }
322
323   /* If this working copy is from a future version, then bail out.  */
324   if (format > SVN_WC__VERSION)
325     {
326       return svn_error_createf(
327         SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
328         _("This client is too old to work with the working copy at\n"
329           "'%s' (format %d).\n"
330           "You need to get a newer Subversion client. For more details, see\n"
331           "  http://subversion.apache.org/faq.html#working-copy-format-change\n"
332           ),
333         svn_dirent_local_style(wcroot_abspath, scratch_pool),
334         format);
335     }
336
337   /* Verify that no work items exists. If they do, then our integrity is
338      suspect and, thus, we cannot upgrade this database.  */
339   if (format >= SVN_WC__HAS_WORK_QUEUE &&
340       format < SVN_WC__VERSION && verify_format)
341     {
342       svn_error_t *err = svn_wc__db_verify_no_work(sdb);
343       if (err)
344         {
345           /* Special message for attempts to upgrade a 1.7-dev wc with
346              outstanding workqueue items. */
347           if (err->apr_err == SVN_ERR_WC_CLEANUP_REQUIRED
348               && format < SVN_WC__VERSION && verify_format)
349             err = svn_error_quick_wrap(err, _("Cleanup with an older 1.7 "
350                                               "client before upgrading with "
351                                               "this client"));
352           return svn_error_trace(err);
353         }
354     }
355
356   /* Auto-upgrade the SDB if possible.  */
357   if (format < SVN_WC__VERSION && verify_format)
358     {
359       return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
360                                _("The working copy at '%s'\nis too old "
361                                  "(format %d) to work with client version "
362                                  "'%s' (expects format %d). You need to "
363                                  "upgrade the working copy first.\n"),
364                                svn_dirent_local_style(wcroot_abspath,
365                                                       scratch_pool),
366                                format, SVN_VERSION, SVN_WC__VERSION);
367     }
368
369   *wcroot = apr_palloc(result_pool, sizeof(**wcroot));
370
371   (*wcroot)->abspath = wcroot_abspath;
372   (*wcroot)->sdb = sdb;
373   (*wcroot)->wc_id = wc_id;
374   (*wcroot)->format = format;
375   /* 8 concurrent locks is probably more than a typical wc_ng based svn client
376      uses. */
377   (*wcroot)->owned_locks = apr_array_make(result_pool, 8,
378                                           sizeof(svn_wc__db_wclock_t));
379   (*wcroot)->access_cache = apr_hash_make(result_pool);
380
381   /* SDB will be NULL for pre-NG working copies. We only need to run a
382      cleanup when the SDB is present.  */
383   if (sdb != NULL)
384     apr_pool_cleanup_register(result_pool, *wcroot, close_wcroot,
385                               apr_pool_cleanup_null);
386   return SVN_NO_ERROR;
387 }
388
389
390 svn_error_t *
391 svn_wc__db_close_many_wcroots(apr_hash_t *roots,
392                               apr_pool_t *state_pool,
393                               apr_pool_t *scratch_pool)
394 {
395   apr_hash_index_t *hi;
396
397   for (hi = apr_hash_first(scratch_pool, roots); hi; hi = apr_hash_next(hi))
398     {
399       svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi);
400       apr_status_t result;
401
402       result = apr_pool_cleanup_run(state_pool, wcroot, close_wcroot);
403       if (result != APR_SUCCESS)
404         return svn_error_wrap_apr(result, NULL);
405     }
406
407   return SVN_NO_ERROR;
408 }
409
410
411 /* POOL may be NULL if the lifetime of LOCAL_ABSPATH is sufficient.  */
412 static const char *
413 compute_relpath(const svn_wc__db_wcroot_t *wcroot,
414                 const char *local_abspath,
415                 apr_pool_t *result_pool)
416 {
417   const char *relpath = svn_dirent_is_child(wcroot->abspath, local_abspath,
418                                             result_pool);
419   if (relpath == NULL)
420     return "";
421   return relpath;
422 }
423
424
425 /* Return in *LINK_TARGET_ABSPATH the absolute path the symlink at
426  * LOCAL_ABSPATH is pointing to. Perform all allocations in POOL. */
427 static svn_error_t *
428 read_link_target(const char **link_target_abspath,
429                  const char *local_abspath,
430                  apr_pool_t *pool)
431 {
432   svn_string_t *link_target;
433   const char *canon_link_target;
434
435   SVN_ERR(svn_io_read_link(&link_target, local_abspath, pool));
436   if (link_target->len == 0)
437     return svn_error_createf(SVN_ERR_WC_NOT_SYMLINK, NULL,
438                              _("The symlink at '%s' points nowhere"),
439                              svn_dirent_local_style(local_abspath, pool));
440
441   canon_link_target = svn_dirent_canonicalize(link_target->data, pool);
442
443   /* Treat relative symlinks as relative to LOCAL_ABSPATH's parent. */
444   if (!svn_dirent_is_absolute(canon_link_target))
445     canon_link_target = svn_dirent_join(svn_dirent_dirname(local_abspath,
446                                                            pool),
447                                         canon_link_target, pool);
448
449   /* Collapse any .. in the symlink part of the path. */
450   if (svn_path_is_backpath_present(canon_link_target))
451     SVN_ERR(svn_dirent_get_absolute(link_target_abspath, canon_link_target,
452                                     pool));
453   else
454     *link_target_abspath = canon_link_target;
455
456   return SVN_NO_ERROR;
457 }
458
459 /* Verify if the sqlite_stat1 table exists and if not tries to add
460    this table (but ignores errors on adding the schema) */
461 static svn_error_t *
462 verify_stats_table(svn_sqlite__db_t *sdb,
463                    int format,
464                    apr_pool_t *scratch_pool)
465 {
466   svn_sqlite__stmt_t *stmt;
467   svn_boolean_t have_row;
468
469   if (format != SVN_WC__ENSURE_STAT1_TABLE)
470     return SVN_NO_ERROR;
471
472   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
473                                     STMT_HAVE_STAT1_TABLE));
474   SVN_ERR(svn_sqlite__step(&have_row, stmt));
475   SVN_ERR(svn_sqlite__reset(stmt));
476
477   if (!have_row)
478     {
479       svn_error_clear(
480           svn_wc__db_install_schema_statistics(sdb, scratch_pool));
481     }
482
483   return SVN_NO_ERROR;
484 }
485
486 /* Sqlite transaction helper for opening the db in
487    svn_wc__db_wcroot_parse_local_abspath() to avoid multiple
488    db operations that each obtain and release a lock */
489 static svn_error_t *
490 fetch_sdb_info(apr_int64_t *wc_id,
491                int *format,
492                svn_sqlite__db_t *sdb,
493                apr_pool_t *scratch_pool)
494 {
495   *wc_id = -1;
496   *format = -1;
497
498   SVN_SQLITE__WITH_LOCK4(
499         svn_wc__db_util_fetch_wc_id(wc_id, sdb, scratch_pool),
500         svn_sqlite__read_schema_version(format, sdb, scratch_pool),
501         verify_stats_table(sdb, *format, scratch_pool),
502         SVN_NO_ERROR,
503         sdb);
504
505   return SVN_NO_ERROR;
506 }
507
508
509 svn_error_t *
510 svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot,
511                                       const char **local_relpath,
512                                       svn_wc__db_t *db,
513                                       const char *local_abspath,
514                                       apr_pool_t *result_pool,
515                                       apr_pool_t *scratch_pool)
516 {
517   const char *local_dir_abspath;
518   const char *original_abspath = local_abspath;
519   svn_node_kind_t kind;
520   const char *build_relpath;
521   svn_wc__db_wcroot_t *probe_wcroot;
522   svn_wc__db_wcroot_t *found_wcroot = NULL;
523   const char *scan_abspath;
524   svn_sqlite__db_t *sdb = NULL;
525   svn_boolean_t moved_upwards = FALSE;
526   svn_boolean_t always_check = FALSE;
527   int wc_format = 0;
528   const char *adm_relpath;
529   /* Non-NULL if WCROOT is found through a symlink: */
530   const char *symlink_wcroot_abspath = NULL;
531   apr_pool_t *iterpool;
532
533   /* ### we need more logic for finding the database (if it is located
534      ### outside of the wcroot) and then managing all of that within DB.
535      ### for now: play quick & dirty. */
536
537   probe_wcroot = svn_hash_gets(db->dir_data, local_abspath);
538   if (probe_wcroot != NULL)
539     {
540       *wcroot = probe_wcroot;
541
542       /* We got lucky. Just return the thing BEFORE performing any I/O.  */
543       /* ### validate SMODE against how we opened wcroot->sdb? and against
544          ### DB->mode? (will we record per-dir mode?)  */
545
546       /* ### for most callers, we could pass NULL for result_pool.  */
547       *local_relpath = compute_relpath(probe_wcroot, local_abspath,
548                                        result_pool);
549
550       return SVN_NO_ERROR;
551     }
552
553   /* ### at some point in the future, we may need to find a way to get
554      ### rid of this stat() call. it is going to happen for EVERY call
555      ### into wc_db which references a file. calls for directories could
556      ### get an early-exit in the hash lookup just above.  */
557   SVN_ERR(get_path_kind(&kind, db, local_abspath, scratch_pool));
558   if (kind != svn_node_dir)
559     {
560       /* If the node specified by the path is NOT present, then it cannot
561          possibly be a directory containing ".svn/wc.db".
562
563          If it is a file, then it cannot contain ".svn/wc.db".
564
565          For both of these cases, strip the basename off of the path and
566          move up one level. Keep record of what we strip, though, since
567          we'll need it later to construct local_relpath.  */
568       svn_dirent_split(&local_dir_abspath, &build_relpath, local_abspath,
569                        scratch_pool);
570
571       /* Is this directory in our hash?  */
572       probe_wcroot = svn_hash_gets(db->dir_data, local_dir_abspath);
573       if (probe_wcroot != NULL)
574         {
575           const char *dir_relpath;
576
577           *wcroot = probe_wcroot;
578
579           /* Stashed directory's local_relpath + basename. */
580           dir_relpath = compute_relpath(probe_wcroot, local_dir_abspath,
581                                         NULL);
582           *local_relpath = svn_relpath_join(dir_relpath,
583                                             build_relpath,
584                                             result_pool);
585           return SVN_NO_ERROR;
586         }
587
588       /* If the requested path is not on the disk, then we don't know how
589          many ancestors need to be scanned until we start hitting content
590          on the disk. Set ALWAYS_CHECK to keep looking for .svn/entries
591          rather than bailing out after the first check.  */
592       if (kind == svn_node_none)
593         always_check = TRUE;
594
595       /* Start the scanning at LOCAL_DIR_ABSPATH.  */
596       local_abspath = local_dir_abspath;
597     }
598   else
599     {
600       /* Start the local_relpath empty. If *this* directory contains the
601          wc.db, then relpath will be the empty string.  */
602       build_relpath = "";
603
604       /* Remember the dir containing LOCAL_ABSPATH (they're the same).  */
605       local_dir_abspath = local_abspath;
606     }
607
608   /* LOCAL_ABSPATH refers to a directory at this point. At this point,
609      we've determined that an associated WCROOT is NOT in the DB's hash
610      table for this directory. Let's find an existing one in the ancestors,
611      or create one when we find the actual wcroot.  */
612
613   /* Assume that LOCAL_ABSPATH is a directory, and look for the SQLite
614      database in the right place. If we find it... great! If not, then
615      peel off some components, and try again. */
616
617   iterpool = svn_pool_create(scratch_pool);
618   adm_relpath = svn_wc_get_adm_dir(scratch_pool);
619   while (TRUE)
620     {
621       svn_error_t *err;
622       svn_node_kind_t adm_subdir_kind;
623
624       const char *adm_subdir;
625
626       svn_pool_clear(iterpool);
627
628       adm_subdir = svn_dirent_join(local_abspath, adm_relpath, iterpool);
629
630       SVN_ERR(svn_io_check_path(adm_subdir, &adm_subdir_kind, iterpool));
631
632       if (adm_subdir_kind == svn_node_dir)
633         {
634           /* We always open the database in read/write mode.  If the database
635              isn't writable in the filesystem, SQLite will internally open
636              it as read-only, and we'll get an error if we try to do a write
637              operation.
638
639              We could decide what to do on a per-operation basis, but since
640              we're caching database handles, it make sense to be as permissive
641              as the filesystem allows. */
642           err = svn_wc__db_util_open_db(&sdb, local_abspath, SDB_FILE,
643                                         svn_sqlite__mode_readwrite,
644                                         db->exclusive, db->timeout, NULL,
645                                         db->state_pool, scratch_pool);
646           if (err == NULL)
647             {
648 #ifdef SVN_DEBUG
649               /* Install self-verification trigger statements. */
650               err = svn_sqlite__exec_statements(sdb,
651                                                 STMT_VERIFICATION_TRIGGERS);
652               if (err && err->apr_err == SVN_ERR_SQLITE_ERROR)
653                 {
654                   /* Verification triggers can fail to install on old 1.7-dev
655                    * formats which didn't have a NODES table yet. Ignore sqlite
656                    * errors so such working copies can be upgraded. */
657                   svn_error_clear(err);
658                 }
659               else
660                 SVN_ERR(err);
661 #endif
662               break;
663             }
664           if (err->apr_err != SVN_ERR_SQLITE_ERROR
665               && !APR_STATUS_IS_ENOENT(err->apr_err))
666             return svn_error_trace(err);
667           svn_error_clear(err);
668
669           /* If we have not moved upwards, then check for a wc-1 working copy.
670              Since wc-1 has a .svn in every directory, and we didn't find one
671              in the original directory, then we aren't looking at a wc-1.
672
673              If the original path is not present, then we have to check on every
674              iteration. The content may be the immediate parent, or possibly
675              five ancetors higher. We don't test for directory presence (just
676              for the presence of subdirs/files), so we don't know when we can
677              stop checking ... so just check always.  */
678           if (!moved_upwards || always_check)
679             {
680               SVN_ERR(get_old_version(&wc_format, local_abspath,
681                                       iterpool));
682               if (wc_format != 0)
683                 break;
684             }
685         }
686
687       /* We couldn't open the SDB within the specified directory, so
688          move up one more directory. */
689       if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
690         {
691           /* Hit the root without finding a wcroot. */
692
693           /* The wcroot could be a symlink to a directory.
694            * (Issue #2557, #3987). If so, try again, this time scanning
695            * for a db within the directory the symlink points to,
696            * rather than within the symlink's parent directory. */
697           if (kind == svn_node_symlink)
698             {
699               svn_node_kind_t resolved_kind;
700
701               local_abspath = original_abspath;
702
703               SVN_ERR(svn_io_check_resolved_path(local_abspath,
704                                                  &resolved_kind,
705                                                  iterpool));
706               if (resolved_kind == svn_node_dir)
707                 {
708                   /* Is this directory recorded in our hash?  */
709                   found_wcroot = svn_hash_gets(db->dir_data, local_abspath);
710                   if (found_wcroot)
711                     break;
712
713                   symlink_wcroot_abspath = local_abspath;
714                   SVN_ERR(read_link_target(&local_abspath, local_abspath,
715                                            scratch_pool));
716 try_symlink_as_dir:
717                   kind = svn_node_dir;
718                   moved_upwards = FALSE;
719                   local_dir_abspath = local_abspath;
720                   build_relpath = "";
721
722                   continue;
723                 }
724             }
725
726           return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
727                                    _("'%s' is not a working copy"),
728                                    svn_dirent_local_style(original_abspath,
729                                                           scratch_pool));
730         }
731
732       local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
733
734       moved_upwards = TRUE;
735       symlink_wcroot_abspath = NULL;
736
737       /* Is the parent directory recorded in our hash?  */
738       found_wcroot = svn_hash_gets(db->dir_data, local_abspath);
739       if (found_wcroot != NULL)
740         break;
741     }
742
743   if (found_wcroot != NULL)
744     {
745       /* We found a hash table entry for an ancestor, so we stopped scanning
746          since all subdirectories use the same WCROOT.  */
747       *wcroot = found_wcroot;
748     }
749   else if (wc_format == 0)
750     {
751       /* We finally found the database. Construct a wcroot_t for it.  */
752
753       apr_int64_t wc_id;
754       int format;
755       svn_error_t *err;
756
757       err = fetch_sdb_info(&wc_id, &format, sdb, scratch_pool);
758       if (err)
759         {
760           if (err->apr_err == SVN_ERR_WC_CORRUPT)
761             return svn_error_quick_wrapf(
762               err, _("Missing a row in WCROOT for '%s'."),
763               svn_dirent_local_style(original_abspath, scratch_pool));
764           return svn_error_trace(err);
765         }
766
767       /* WCROOT.local_abspath may be NULL when the database is stored
768          inside the wcroot, but we know the abspath is this directory
769          (ie. where we found it).  */
770
771       err = svn_wc__db_pdh_create_wcroot(wcroot,
772                             apr_pstrdup(db->state_pool,
773                                         symlink_wcroot_abspath
774                                           ? symlink_wcroot_abspath
775                                           : local_abspath),
776                             sdb, wc_id, format,
777                             db->verify_format,
778                             db->state_pool, scratch_pool);
779       if (err && (err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT ||
780                   err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) &&
781           kind == svn_node_symlink)
782         {
783           /* We found an unsupported WC after traversing upwards from a
784            * symlink. Fall through to code below to check if the symlink
785            * points at a supported WC. */
786           svn_error_clear(err);
787           *wcroot = NULL;
788         }
789       else if (err)
790         {
791           /* Close handle if we are not going to use it to support
792              upgrading with exclusive wc locking. */
793           return svn_error_compose_create(err, svn_sqlite__close(sdb));
794         }
795     }
796   else
797     {
798       /* We found something that looks like a wc-1 working copy directory.
799          However, if the format version is 12 and the .svn/entries file
800          is only 3 bytes long, then it's a breadcrumb in a wc-ng working
801          copy that's missing an .svn/wc.db, or its .svn/wc.db is corrupt. */
802       if (wc_format == SVN_WC__WC_NG_VERSION /* 12 */)
803         {
804           apr_finfo_t info;
805
806           /* Check attributes of .svn/entries */
807           const char *admin_abspath = svn_wc__adm_child(
808               local_abspath, SVN_WC__ADM_ENTRIES, scratch_pool);
809           svn_error_t *err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE,
810                                          scratch_pool);
811
812           /* If the former does not succeed, something is seriously wrong. */
813           if (err)
814             return svn_error_createf(
815                 SVN_ERR_WC_CORRUPT, err,
816                 _("The working copy at '%s' is corrupt."),
817                 svn_dirent_local_style(local_abspath, scratch_pool));
818           svn_error_clear(err);
819
820           if (3 == info.size)
821             {
822               /* Check existence of .svn/wc.db */
823               admin_abspath = svn_wc__adm_child(local_abspath, SDB_FILE,
824                                                 scratch_pool);
825               err = svn_io_stat(&info, admin_abspath, APR_FINFO_SIZE,
826                                 scratch_pool);
827               if (err && APR_STATUS_IS_ENOENT(err->apr_err))
828                 {
829                   svn_error_clear(err);
830                   return svn_error_createf(
831                       SVN_ERR_WC_CORRUPT, NULL,
832                       _("The working copy database at '%s' is missing."),
833                       svn_dirent_local_style(local_abspath, scratch_pool));
834                 }
835               else
836                 /* We should never have reached this point in the code
837                    if .svn/wc.db exists; therefore it's best to assume
838                    it's corrupt. */
839                 return svn_error_createf(
840                     SVN_ERR_WC_CORRUPT, err,
841                     _("The working copy database at '%s' is corrupt."),
842                     svn_dirent_local_style(local_abspath, scratch_pool));
843             }
844         }
845
846       SVN_ERR(svn_wc__db_pdh_create_wcroot(wcroot,
847                             apr_pstrdup(db->state_pool,
848                                         symlink_wcroot_abspath
849                                           ? symlink_wcroot_abspath
850                                           : local_abspath),
851                             NULL, UNKNOWN_WC_ID, wc_format,
852                             db->verify_format,
853                             db->state_pool, scratch_pool));
854     }
855
856   if (*wcroot)
857     {
858       const char *dir_relpath;
859
860       if (symlink_wcroot_abspath)
861         {
862           /* The WCROOT was found through a symlink pointing at the root of
863            * the WC. Cache the WCROOT under the symlink's path. */
864           local_dir_abspath = symlink_wcroot_abspath;
865         }
866
867       /* The subdirectory's relpath is easily computed relative to the
868          wcroot that we just found.  */
869       dir_relpath = compute_relpath(*wcroot, local_dir_abspath, NULL);
870
871       /* And the result local_relpath may include a filename.  */
872       *local_relpath = svn_relpath_join(dir_relpath, build_relpath, result_pool);
873     }
874
875   if (kind == svn_node_symlink)
876     {
877       svn_boolean_t retry_if_dir = FALSE;
878       svn_wc__db_status_t status;
879       svn_boolean_t conflicted;
880       svn_error_t *err;
881
882       /* Check if the symlink is versioned or obstructs a versioned node
883        * in this DB -- in that case, use this wcroot. Else, if the symlink
884        * points to a directory, try to find a wcroot in that directory
885        * instead. */
886
887       if (*wcroot)
888         {
889           err = svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL,
890                                               NULL, NULL, NULL, NULL, NULL,
891                                               NULL, NULL, NULL, NULL, NULL,
892                                               NULL, NULL, NULL, &conflicted,
893                                               NULL, NULL, NULL, NULL, NULL,
894                                               NULL, *wcroot, *local_relpath,
895                                               scratch_pool, scratch_pool);
896           if (err)
897             {
898               if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
899                   && !SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
900                 return svn_error_trace(err);
901
902               svn_error_clear(err);
903               retry_if_dir = TRUE; /* The symlink is unversioned. */
904             }
905           else
906             {
907               /* The symlink is versioned, or obstructs a versioned node.
908                * Ignore non-conflicted not-present/excluded nodes.
909                * This allows the symlink to redirect the wcroot query to a
910                * directory, regardless of 'invisible' nodes in this WC. */
911               retry_if_dir = ((status == svn_wc__db_status_not_present ||
912                                status == svn_wc__db_status_excluded ||
913                                status == svn_wc__db_status_server_excluded)
914                               && !conflicted);
915             }
916         }
917       else
918         retry_if_dir = TRUE;
919
920       if (retry_if_dir)
921         {
922           svn_node_kind_t resolved_kind;
923
924           SVN_ERR(svn_io_check_resolved_path(original_abspath,
925                                              &resolved_kind,
926                                              scratch_pool));
927           if (resolved_kind == svn_node_dir)
928             {
929               symlink_wcroot_abspath = original_abspath;
930               SVN_ERR(read_link_target(&local_abspath, original_abspath,
931                                        scratch_pool));
932               /* This handle was opened in this function but is not going
933                  to be used further so close it. */
934               if (sdb)
935                 SVN_ERR(svn_sqlite__close(sdb));
936               goto try_symlink_as_dir;
937             }
938         }
939     }
940
941   /* We've found the appropriate WCROOT for the requested path. Stash
942      it into that path's directory.  */
943   svn_hash_sets(db->dir_data,
944                 apr_pstrdup(db->state_pool, local_dir_abspath),
945                 *wcroot);
946
947   /* Did we traverse up to parent directories?  */
948   if (!moved_upwards)
949     {
950       /* We did NOT move to a parent of the original requested directory.
951          We've constructed and filled in a WCROOT for the request, so we
952          are done.  */
953       return SVN_NO_ERROR;
954     }
955
956   /* The WCROOT that we just found/built was for the LOCAL_ABSPATH originally
957      passed into this function. We stepped *at least* one directory above that.
958      We should now associate the WROOT for each parent directory that does
959      not (yet) have one.  */
960
961   scan_abspath = local_dir_abspath;
962
963   do
964     {
965       const char *parent_dir = svn_dirent_dirname(scan_abspath, scratch_pool);
966       svn_wc__db_wcroot_t *parent_wcroot;
967
968       parent_wcroot = svn_hash_gets(db->dir_data, parent_dir);
969       if (parent_wcroot == NULL)
970         {
971           svn_hash_sets(db->dir_data, apr_pstrdup(db->state_pool, parent_dir),
972                         *wcroot);
973         }
974
975       /* Move up a directory, stopping when we reach the directory where
976          we found/built the WCROOT.  */
977       scan_abspath = parent_dir;
978     }
979   while (strcmp(scan_abspath, local_abspath) != 0);
980
981   svn_pool_destroy(iterpool);
982   return SVN_NO_ERROR;
983 }
984
985
986 svn_error_t *
987 svn_wc__db_drop_root(svn_wc__db_t *db,
988                      const char *local_abspath,
989                      apr_pool_t *scratch_pool)
990 {
991   svn_wc__db_wcroot_t *root_wcroot = svn_hash_gets(db->dir_data, local_abspath);
992   apr_hash_index_t *hi;
993   apr_status_t result;
994
995   if (!root_wcroot)
996     return SVN_NO_ERROR;
997
998   if (strcmp(root_wcroot->abspath, local_abspath) != 0)
999     return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
1000                              _("'%s' is not a working copy root"),
1001                              svn_dirent_local_style(local_abspath,
1002                                                     scratch_pool));
1003
1004   for (hi = apr_hash_first(scratch_pool, db->dir_data);
1005        hi;
1006        hi = apr_hash_next(hi))
1007     {
1008       svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi);
1009
1010       if (wcroot == root_wcroot)
1011         svn_hash_sets(db->dir_data, apr_hash_this_key(hi), NULL);
1012     }
1013
1014   result = apr_pool_cleanup_run(db->state_pool, root_wcroot, close_wcroot);
1015   if (result != APR_SUCCESS)
1016     return svn_error_wrap_apr(result, NULL);
1017
1018   return SVN_NO_ERROR;
1019 }