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