wc_db_wcroot.c revision 362181
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
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.  */
50static svn_error_t *
51get_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. */
108static svn_error_t *
109get_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
149svn_error_t *
150svn_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 */
168static svn_error_t *
169verify_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/* */
189static apr_status_t
190close_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
222svn_error_t *
223svn_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
270svn_error_t *
271svn_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
297svn_error_t *
298svn_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
390svn_error_t *
391svn_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.  */
412static const char *
413compute_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. */
427static svn_error_t *
428read_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) */
461static svn_error_t *
462verify_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 */
489static svn_error_t *
490fetch_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
509svn_error_t *
510svn_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));
716try_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
986svn_error_t *
987svn_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}
1020