1251881Speter/* fs.c --- creating, opening and closing filesystems
2251881Speter *
3251881Speter * ====================================================================
4251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
5251881Speter *    or more contributor license agreements.  See the NOTICE file
6251881Speter *    distributed with this work for additional information
7251881Speter *    regarding copyright ownership.  The ASF licenses this file
8251881Speter *    to you under the Apache License, Version 2.0 (the
9251881Speter *    "License"); you may not use this file except in compliance
10251881Speter *    with the License.  You may obtain a copy of the License at
11251881Speter *
12251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
13251881Speter *
14251881Speter *    Unless required by applicable law or agreed to in writing,
15251881Speter *    software distributed under the License is distributed on an
16251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17251881Speter *    KIND, either express or implied.  See the License for the
18251881Speter *    specific language governing permissions and limitations
19251881Speter *    under the License.
20251881Speter * ====================================================================
21251881Speter */
22251881Speter
23251881Speter#include <stdlib.h>
24251881Speter#include <stdio.h>
25251881Speter#include <string.h>
26251881Speter
27251881Speter#include <apr_general.h>
28251881Speter#include <apr_pools.h>
29251881Speter#include <apr_file_io.h>
30251881Speter
31251881Speter#include "svn_hash.h"
32251881Speter#include "svn_pools.h"
33251881Speter#include "svn_fs.h"
34251881Speter#include "svn_path.h"
35251881Speter#include "svn_utf.h"
36251881Speter#include "svn_delta.h"
37251881Speter#include "svn_version.h"
38251881Speter#include "fs.h"
39251881Speter#include "err.h"
40251881Speter#include "dag.h"
41251881Speter#include "revs-txns.h"
42251881Speter#include "uuid.h"
43251881Speter#include "tree.h"
44251881Speter#include "id.h"
45251881Speter#include "lock.h"
46251881Speter#define SVN_WANT_BDB
47251881Speter#include "svn_private_config.h"
48251881Speter
49251881Speter#include "bdb/bdb-err.h"
50251881Speter#include "bdb/bdb_compat.h"
51251881Speter#include "bdb/env.h"
52251881Speter#include "bdb/nodes-table.h"
53251881Speter#include "bdb/rev-table.h"
54251881Speter#include "bdb/txn-table.h"
55251881Speter#include "bdb/copies-table.h"
56251881Speter#include "bdb/changes-table.h"
57251881Speter#include "bdb/reps-table.h"
58251881Speter#include "bdb/strings-table.h"
59251881Speter#include "bdb/uuids-table.h"
60251881Speter#include "bdb/locks-table.h"
61251881Speter#include "bdb/lock-tokens-table.h"
62251881Speter#include "bdb/node-origins-table.h"
63251881Speter#include "bdb/miscellaneous-table.h"
64251881Speter#include "bdb/checksum-reps-table.h"
65251881Speter
66251881Speter#include "../libsvn_fs/fs-loader.h"
67251881Speter#include "private/svn_fs_util.h"
68251881Speter
69251881Speter
70251881Speter/* Checking for return values, and reporting errors.  */
71251881Speter
72251881Speter/* Check that we're using the right Berkeley DB version. */
73251881Speter/* FIXME: This check should be abstracted into the DB back-end layer. */
74251881Speterstatic svn_error_t *
75251881Spetercheck_bdb_version(void)
76251881Speter{
77251881Speter  int major, minor, patch;
78251881Speter
79251881Speter  db_version(&major, &minor, &patch);
80251881Speter
81251881Speter  /* First, check that we're using a reasonably correct of Berkeley DB. */
82251881Speter  if ((major < SVN_FS_WANT_DB_MAJOR)
83251881Speter      || (major == SVN_FS_WANT_DB_MAJOR && minor < SVN_FS_WANT_DB_MINOR)
84251881Speter      || (major == SVN_FS_WANT_DB_MAJOR && minor == SVN_FS_WANT_DB_MINOR
85251881Speter          && patch < SVN_FS_WANT_DB_PATCH))
86251881Speter    return svn_error_createf(SVN_ERR_FS_GENERAL, 0,
87251881Speter                             _("Bad database version: got %d.%d.%d,"
88251881Speter                               " should be at least %d.%d.%d"),
89251881Speter                             major, minor, patch,
90251881Speter                             SVN_FS_WANT_DB_MAJOR,
91251881Speter                             SVN_FS_WANT_DB_MINOR,
92251881Speter                             SVN_FS_WANT_DB_PATCH);
93251881Speter
94251881Speter  /* Now, check that the version we're running against is the same as
95251881Speter     the one we compiled with. */
96251881Speter  if (major != DB_VERSION_MAJOR || minor != DB_VERSION_MINOR)
97251881Speter    return svn_error_createf(SVN_ERR_FS_GENERAL, 0,
98251881Speter                             _("Bad database version:"
99251881Speter                               " compiled with %d.%d.%d,"
100251881Speter                               " running against %d.%d.%d"),
101251881Speter                             DB_VERSION_MAJOR,
102251881Speter                             DB_VERSION_MINOR,
103251881Speter                             DB_VERSION_PATCH,
104251881Speter                             major, minor, patch);
105251881Speter  return SVN_NO_ERROR;
106251881Speter}
107251881Speter
108251881Speter
109251881Speter
110251881Speter/* Cleanup functions.  */
111251881Speter
112251881Speter/* Close a database in the filesystem FS.
113251881Speter   DB_PTR is a pointer to the DB pointer in *FS to close.
114251881Speter   NAME is the name of the database, for use in error messages.  */
115251881Speterstatic svn_error_t *
116251881Spetercleanup_fs_db(svn_fs_t *fs, DB **db_ptr, const char *name)
117251881Speter{
118251881Speter  /* If the BDB environment is panicked, don't do anything, since
119251881Speter     attempting to close the database will fail anyway. */
120251881Speter  base_fs_data_t *bfd = fs->fsap_data;
121251881Speter  if (*db_ptr && !svn_fs_bdb__get_panic(bfd->bdb))
122251881Speter    {
123251881Speter      DB *db = *db_ptr;
124251881Speter      char *msg = apr_psprintf(fs->pool, "closing '%s' database", name);
125251881Speter      int db_err;
126251881Speter
127251881Speter      *db_ptr = 0;
128251881Speter      db_err = db->close(db, 0);
129251881Speter      if (db_err == DB_RUNRECOVERY)
130251881Speter        {
131251881Speter          /* We can ignore DB_RUNRECOVERY errors from DB->close, but
132251881Speter             must set the panic flag in the environment baton.  The
133251881Speter             error will be propagated appropriately from
134251881Speter             svn_fs_bdb__close. */
135251881Speter          svn_fs_bdb__set_panic(bfd->bdb);
136251881Speter          db_err = 0;
137251881Speter        }
138251881Speter
139251881Speter#if SVN_BDB_HAS_DB_INCOMPLETE
140251881Speter      /* We can ignore DB_INCOMPLETE on db->close and db->sync; it
141251881Speter       * just means someone else was using the db at the same time
142251881Speter       * we were.  See the Berkeley documentation at:
143251881Speter       * http://www.sleepycat.com/docs/ref/program/errorret.html#DB_INCOMPLETE
144251881Speter       * http://www.sleepycat.com/docs/api_c/db_close.html
145251881Speter       */
146251881Speter      if (db_err == DB_INCOMPLETE)
147251881Speter        db_err = 0;
148251881Speter#endif /* SVN_BDB_HAS_DB_INCOMPLETE */
149251881Speter
150251881Speter      SVN_ERR(BDB_WRAP(fs, msg, db_err));
151251881Speter    }
152251881Speter
153251881Speter  return SVN_NO_ERROR;
154251881Speter}
155251881Speter
156251881Speter/* Close whatever Berkeley DB resources are allocated to FS.  */
157251881Speterstatic svn_error_t *
158251881Spetercleanup_fs(svn_fs_t *fs)
159251881Speter{
160251881Speter  base_fs_data_t *bfd = fs->fsap_data;
161251881Speter  bdb_env_baton_t *bdb = (bfd ? bfd->bdb : NULL);
162251881Speter
163251881Speter  if (!bdb)
164251881Speter    return SVN_NO_ERROR;
165251881Speter
166251881Speter  /* Close the databases.  */
167251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->nodes, "nodes"));
168251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->revisions, "revisions"));
169251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->transactions, "transactions"));
170251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->copies, "copies"));
171251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->changes, "changes"));
172251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->representations, "representations"));
173251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->strings, "strings"));
174251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->uuids, "uuids"));
175251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->locks, "locks"));
176251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->lock_tokens, "lock-tokens"));
177251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->node_origins, "node-origins"));
178251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->checksum_reps, "checksum-reps"));
179251881Speter  SVN_ERR(cleanup_fs_db(fs, &bfd->miscellaneous, "miscellaneous"));
180251881Speter
181251881Speter  /* Finally, close the environment.  */
182251881Speter  bfd->bdb = 0;
183251881Speter  {
184251881Speter    svn_error_t *err = svn_fs_bdb__close(bdb);
185251881Speter    if (err)
186251881Speter      return svn_error_createf
187251881Speter        (err->apr_err, err,
188251881Speter         _("Berkeley DB error for filesystem '%s'"
189251881Speter           " while closing environment:\n"),
190251881Speter         fs->path);
191251881Speter  }
192251881Speter  return SVN_NO_ERROR;
193251881Speter}
194251881Speter
195251881Speter#if 0   /* Set to 1 for instrumenting. */
196251881Speterstatic void print_fs_stats(svn_fs_t *fs)
197251881Speter{
198251881Speter  base_fs_data_t *bfd = fs->fsap_data;
199251881Speter  DB_TXN_STAT *t;
200251881Speter  DB_LOCK_STAT *l;
201251881Speter  int db_err;
202251881Speter
203251881Speter  /* Print transaction statistics for this DB env. */
204251881Speter  if ((db_err = bfd->bdb->env->txn_stat(bfd->bdb->env, &t, 0)) != 0)
205251881Speter    fprintf(stderr, "Error running bfd->bdb->env->txn_stat(): %s",
206251881Speter            db_strerror(db_err));
207251881Speter  else
208251881Speter    {
209251881Speter      printf("*** DB transaction stats, right before closing env:\n");
210251881Speter      printf("   Number of transactions currently active: %d\n",
211251881Speter             t->st_nactive);
212251881Speter      printf("   Max number of active transactions at any one time: %d\n",
213251881Speter             t->st_maxnactive);
214251881Speter      printf("   Number of transactions that have begun: %d\n",
215251881Speter             t->st_nbegins);
216251881Speter      printf("   Number of transactions that have aborted: %d\n",
217251881Speter             t->st_naborts);
218251881Speter      printf("   Number of transactions that have committed: %d\n",
219251881Speter             t->st_ncommits);
220251881Speter      printf("   Number of times a thread was forced to wait: %d\n",
221251881Speter             t->st_region_wait);
222251881Speter      printf("   Number of times a thread didn't need to wait: %d\n",
223251881Speter             t->st_region_nowait);
224251881Speter      printf("*** End DB transaction stats.\n\n");
225251881Speter    }
226251881Speter
227251881Speter  /* Print transaction statistics for this DB env. */
228251881Speter  if ((db_err = bfd->bdb->env->lock_stat(bfd->bdb->env, &l, 0)) != 0)
229251881Speter    fprintf(stderr, "Error running bfd->bdb->env->lock_stat(): %s",
230251881Speter            db_strerror(db_err));
231251881Speter  else
232251881Speter    {
233251881Speter      printf("*** DB lock stats, right before closing env:\n");
234251881Speter      printf("   The number of current locks: %d\n",
235251881Speter             l->st_nlocks);
236251881Speter      printf("   Max number of locks at any one time: %d\n",
237251881Speter             l->st_maxnlocks);
238251881Speter      printf("   Number of current lockers: %d\n",
239251881Speter             l->st_nlockers);
240251881Speter      printf("   Max number of lockers at any one time: %d\n",
241251881Speter             l->st_maxnlockers);
242251881Speter      printf("   Number of current objects: %d\n",
243251881Speter             l->st_nobjects);
244251881Speter      printf("   Max number of objects at any one time: %d\n",
245251881Speter             l->st_maxnobjects);
246251881Speter      printf("   Total number of locks requested: %d\n",
247251881Speter             l->st_nrequests);
248251881Speter      printf("   Total number of locks released: %d\n",
249251881Speter             l->st_nreleases);
250251881Speter      printf("   Total number of lock reqs failed because "
251251881Speter             "DB_LOCK_NOWAIT was set: %d\n", l->st_nnowaits);
252251881Speter      printf("   Total number of locks not immediately available "
253251881Speter             "due to conflicts: %d\n", l->st_nconflicts);
254251881Speter      printf("   Number of deadlocks detected: %d\n", l->st_ndeadlocks);
255251881Speter      printf("   Number of times a thread waited before "
256251881Speter             "obtaining the region lock: %d\n", l->st_region_wait);
257251881Speter      printf("   Number of times a thread didn't have to wait: %d\n",
258251881Speter             l->st_region_nowait);
259251881Speter      printf("*** End DB lock stats.\n\n");
260251881Speter    }
261251881Speter
262251881Speter}
263251881Speter#else
264251881Speter#  define print_fs_stats(fs)
265251881Speter#endif /* 0/1 */
266251881Speter
267251881Speter/* An APR pool cleanup function for a filesystem.  DATA must be a
268251881Speter   pointer to the filesystem to clean up.
269251881Speter
270251881Speter   When the filesystem object's pool is freed, we want the resources
271251881Speter   held by Berkeley DB to go away, just like everything else.  So we
272251881Speter   register this cleanup function with the filesystem's pool, and let
273251881Speter   it take care of closing the databases, the environment, and any
274251881Speter   other DB objects we might be using.  APR calls this function before
275251881Speter   actually freeing the pool's memory.
276251881Speter
277251881Speter   It's a pity that we can't return an svn_error_t object from an APR
278251881Speter   cleanup function.  For now, we return the rather generic
279251881Speter   SVN_ERR_FS_CLEANUP, and pass the real svn_error_t to the registered
280251881Speter   warning callback.  */
281251881Speter
282251881Speterstatic apr_status_t
283251881Spetercleanup_fs_apr(void *data)
284251881Speter{
285251881Speter  svn_fs_t *fs = data;
286251881Speter  svn_error_t *err;
287251881Speter
288251881Speter  print_fs_stats(fs);
289251881Speter
290251881Speter  err = cleanup_fs(fs);
291251881Speter  if (! err)
292251881Speter    return APR_SUCCESS;
293251881Speter
294251881Speter  /* Darn. An error during cleanup. Call the warning handler to
295251881Speter     try and do something "right" with this error. Note that
296251881Speter     the default will simply abort().  */
297251881Speter  (*fs->warning)(fs->warning_baton, err);
298251881Speter
299251881Speter  svn_error_clear(err);
300251881Speter
301251881Speter  return SVN_ERR_FS_CLEANUP;
302251881Speter}
303251881Speter
304251881Speter
305251881Speterstatic svn_error_t *
306251881Speterbase_bdb_set_errcall(svn_fs_t *fs,
307251881Speter                     void (*db_errcall_fcn)(const char *errpfx, char *msg))
308251881Speter{
309251881Speter  base_fs_data_t *bfd = fs->fsap_data;
310251881Speter
311251881Speter  SVN_ERR(svn_fs__check_fs(fs, TRUE));
312251881Speter  bfd->bdb->error_info->user_callback = db_errcall_fcn;
313251881Speter
314251881Speter  return SVN_NO_ERROR;
315251881Speter}
316251881Speter
317251881Speter
318251881Speter/* Write the DB_CONFIG file. */
319251881Speterstatic svn_error_t *
320251881Speterbdb_write_config(svn_fs_t *fs)
321251881Speter{
322251881Speter  const char *dbconfig_file_name =
323251881Speter    svn_dirent_join(fs->path, BDB_CONFIG_FILE, fs->pool);
324251881Speter  apr_file_t *dbconfig_file = NULL;
325251881Speter  int i;
326251881Speter
327251881Speter  static const char dbconfig_contents[] =
328251881Speter    "# This is the configuration file for the Berkeley DB environment\n"
329251881Speter    "# used by your Subversion repository.\n"
330251881Speter    "# You must run 'svnadmin recover' whenever you modify this file,\n"
331251881Speter    "# for your changes to take effect.\n"
332251881Speter    "\n"
333251881Speter    "### Lock subsystem\n"
334251881Speter    "#\n"
335251881Speter    "# Make sure you read the documentation at:\n"
336251881Speter    "#\n"
337251881Speter    "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/lock_max.html\n"
338251881Speter    "#\n"
339251881Speter    "# before tweaking these values.\n"
340251881Speter    "#\n"
341251881Speter    "set_lk_max_locks   2000\n"
342251881Speter    "set_lk_max_lockers 2000\n"
343251881Speter    "set_lk_max_objects 2000\n"
344251881Speter    "\n"
345251881Speter    "### Log file subsystem\n"
346251881Speter    "#\n"
347251881Speter    "# Make sure you read the documentation at:\n"
348251881Speter    "#\n"
349251881Speter    "#   http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_bsize.html\n"
350251881Speter    "#   http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_max.html\n"
351251881Speter    "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_limits.html\n"
352251881Speter    "#\n"
353251881Speter    "# Increase the size of the in-memory log buffer from the default\n"
354251881Speter    "# of 32 Kbytes to 256 Kbytes.  Decrease the log file size from\n"
355251881Speter    "# 10 Mbytes to 1 Mbyte.  This will help reduce the amount of disk\n"
356251881Speter    "# space required for hot backups.  The size of the log file must be\n"
357251881Speter    "# at least four times the size of the in-memory log buffer.\n"
358251881Speter    "#\n"
359251881Speter    "# Note: Decreasing the in-memory buffer size below 256 Kbytes will hurt\n"
360251881Speter    "# hurt commit performance. For details, see:\n"
361251881Speter    "#\n"
362251881Speter    "#   http://svn.haxx.se/dev/archive-2002-02/0141.shtml\n"
363251881Speter    "#\n"
364251881Speter    "set_lg_bsize     262144\n"
365251881Speter    "set_lg_max      1048576\n"
366251881Speter    "#\n"
367251881Speter    "# If you see \"log region out of memory\" errors, bump lg_regionmax.\n"
368251881Speter    "# For more information, see:\n"
369251881Speter    "#\n"
370251881Speter    "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
371251881Speter    "#   http://svn.haxx.se/users/archive-2004-10/1000.shtml\n"
372251881Speter    "#\n"
373251881Speter    "set_lg_regionmax 131072\n"
374251881Speter    "#\n"
375251881Speter    /* ### Configure this with "svnadmin create --bdb-cache-size" */
376251881Speter    "# The default cache size in BDB is only 256k. As explained in\n"
377251881Speter    "# http://svn.haxx.se/dev/archive-2004-12/0368.shtml, this is too\n"
378251881Speter    "# small for most applications. Bump this number if \"db_stat -m\"\n"
379251881Speter    "# shows too many cache misses.\n"
380251881Speter    "#\n"
381251881Speter    "set_cachesize    0 1048576 1\n";
382251881Speter
383251881Speter  /* Run-time configurable options.
384251881Speter     Each option set consists of a minimum required BDB version, a
385251881Speter     config hash key, a header, an inactive form and an active
386251881Speter     form. We always write the header; then, depending on the
387251881Speter     run-time configuration and the BDB version we're compiling
388251881Speter     against, we write either the active or inactive form of the
389251881Speter     value. */
390251881Speter  static const struct
391251881Speter  {
392251881Speter    int bdb_major;
393251881Speter    int bdb_minor;
394251881Speter    const char *config_key;
395251881Speter    const char *header;
396251881Speter    const char *inactive;
397251881Speter    const char *active;
398251881Speter  } dbconfig_options[] = {
399251881Speter    /* Controlled by "svnadmin create --bdb-txn-nosync" */
400251881Speter    { 4, 0, SVN_FS_CONFIG_BDB_TXN_NOSYNC,
401251881Speter      /* header */
402251881Speter      "#\n"
403251881Speter      "# Disable fsync of log files on transaction commit. Read the\n"
404251881Speter      "# documentation about DB_TXN_NOSYNC at:\n"
405251881Speter      "#\n"
406251881Speter      "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
407251881Speter      "#\n"
408251881Speter      "# [requires Berkeley DB 4.0]\n"
409251881Speter      "#\n",
410251881Speter      /* inactive */
411251881Speter      "#set_flags DB_TXN_NOSYNC\n",
412251881Speter      /* active */
413251881Speter      "set_flags DB_TXN_NOSYNC\n" },
414251881Speter    /* Controlled by "svnadmin create --bdb-log-keep" */
415251881Speter    { 4, 2, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE,
416251881Speter      /* header */
417251881Speter      "#\n"
418251881Speter      "# Enable automatic removal of unused transaction log files.\n"
419251881Speter      "# Read the documentation about DB_LOG_AUTOREMOVE at:\n"
420251881Speter      "#\n"
421251881Speter      "#   http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
422251881Speter      "#\n"
423251881Speter      "# [requires Berkeley DB 4.2]\n"
424251881Speter      "#\n",
425251881Speter      /* inactive */
426251881Speter      "#set_flags DB_LOG_AUTOREMOVE\n",
427251881Speter      /* active */
428251881Speter      "set_flags DB_LOG_AUTOREMOVE\n" },
429251881Speter  };
430251881Speter  static const int dbconfig_options_length =
431251881Speter    sizeof(dbconfig_options)/sizeof(*dbconfig_options);
432251881Speter
433251881Speter
434251881Speter  SVN_ERR(svn_io_file_open(&dbconfig_file, dbconfig_file_name,
435251881Speter                           APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
436251881Speter                           fs->pool));
437251881Speter
438251881Speter  SVN_ERR(svn_io_file_write_full(dbconfig_file, dbconfig_contents,
439251881Speter                                 sizeof(dbconfig_contents) - 1, NULL,
440251881Speter                                 fs->pool));
441251881Speter
442251881Speter  /* Write the variable DB_CONFIG flags. */
443251881Speter  for (i = 0; i < dbconfig_options_length; ++i)
444251881Speter    {
445251881Speter      void *value = NULL;
446251881Speter      const char *choice;
447251881Speter
448251881Speter      if (fs->config)
449251881Speter        {
450251881Speter          value = svn_hash_gets(fs->config, dbconfig_options[i].config_key);
451251881Speter        }
452251881Speter
453251881Speter      SVN_ERR(svn_io_file_write_full(dbconfig_file,
454251881Speter                                     dbconfig_options[i].header,
455251881Speter                                     strlen(dbconfig_options[i].header),
456251881Speter                                     NULL, fs->pool));
457251881Speter
458251881Speter      if (((DB_VERSION_MAJOR == dbconfig_options[i].bdb_major
459251881Speter            && DB_VERSION_MINOR >= dbconfig_options[i].bdb_minor)
460251881Speter           || DB_VERSION_MAJOR > dbconfig_options[i].bdb_major)
461251881Speter          && value != NULL && strcmp(value, "0") != 0)
462251881Speter        choice = dbconfig_options[i].active;
463251881Speter      else
464251881Speter        choice = dbconfig_options[i].inactive;
465251881Speter
466251881Speter      SVN_ERR(svn_io_file_write_full(dbconfig_file, choice, strlen(choice),
467251881Speter                                     NULL, fs->pool));
468251881Speter    }
469251881Speter
470251881Speter  return svn_io_file_close(dbconfig_file, fs->pool);
471251881Speter}
472251881Speter
473251881Speterstatic svn_error_t *
474299742Sdimbase_bdb_info_format(int *fs_format,
475299742Sdim                     svn_version_t **supports_version,
476299742Sdim                     svn_fs_t *fs,
477299742Sdim                     apr_pool_t *result_pool,
478299742Sdim                     apr_pool_t *scratch_pool)
479299742Sdim{
480299742Sdim  base_fs_data_t *bfd = fs->fsap_data;
481299742Sdim
482299742Sdim  *fs_format = bfd->format;
483299742Sdim  *supports_version = apr_palloc(result_pool, sizeof(svn_version_t));
484299742Sdim
485299742Sdim  (*supports_version)->major = SVN_VER_MAJOR;
486299742Sdim  (*supports_version)->minor = 0;
487299742Sdim  (*supports_version)->patch = 0;
488299742Sdim  (*supports_version)->tag = "";
489299742Sdim
490299742Sdim  switch (bfd->format)
491299742Sdim    {
492299742Sdim    case 1:
493299742Sdim      break;
494299742Sdim    case 2:
495299742Sdim      (*supports_version)->minor = 4;
496299742Sdim      break;
497299742Sdim    case 3:
498299742Sdim      (*supports_version)->minor = 5;
499299742Sdim      break;
500299742Sdim    case 4:
501299742Sdim      (*supports_version)->minor = 6;
502299742Sdim      break;
503299742Sdim#ifdef SVN_DEBUG
504299742Sdim# if SVN_FS_BASE__FORMAT_NUMBER != 4
505299742Sdim#  error "Need to add a 'case' statement here"
506299742Sdim# endif
507299742Sdim#endif
508299742Sdim    }
509299742Sdim
510299742Sdim  return SVN_NO_ERROR;
511299742Sdim}
512299742Sdim
513299742Sdimstatic svn_error_t *
514299742Sdimbase_bdb_info_config_files(apr_array_header_t **files,
515299742Sdim                           svn_fs_t *fs,
516299742Sdim                           apr_pool_t *result_pool,
517299742Sdim                           apr_pool_t *scratch_pool)
518299742Sdim{
519299742Sdim  *files = apr_array_make(result_pool, 1, sizeof(const char *));
520299742Sdim  APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path,
521299742Sdim                                                         BDB_CONFIG_FILE,
522299742Sdim                                                         result_pool);
523299742Sdim  return SVN_NO_ERROR;
524299742Sdim}
525299742Sdim
526299742Sdimstatic svn_error_t *
527251881Speterbase_bdb_verify_root(svn_fs_root_t *root,
528251881Speter                     apr_pool_t *scratch_pool)
529251881Speter{
530251881Speter  /* Verifying is currently a no op for BDB. */
531251881Speter  return SVN_NO_ERROR;
532251881Speter}
533251881Speter
534251881Speterstatic svn_error_t *
535251881Speterbase_bdb_freeze(svn_fs_t *fs,
536251881Speter                svn_fs_freeze_func_t freeze_func,
537251881Speter                void *freeze_baton,
538251881Speter                apr_pool_t *pool)
539251881Speter{
540251881Speter  SVN__NOT_IMPLEMENTED();
541251881Speter}
542251881Speter
543251881Speter
544251881Speter/* Creating a new filesystem */
545251881Speter
546251881Speterstatic fs_vtable_t fs_vtable = {
547251881Speter  svn_fs_base__youngest_rev,
548251881Speter  svn_fs_base__revision_prop,
549251881Speter  svn_fs_base__revision_proplist,
550251881Speter  svn_fs_base__change_rev_prop,
551251881Speter  svn_fs_base__set_uuid,
552251881Speter  svn_fs_base__revision_root,
553251881Speter  svn_fs_base__begin_txn,
554251881Speter  svn_fs_base__open_txn,
555251881Speter  svn_fs_base__purge_txn,
556251881Speter  svn_fs_base__list_transactions,
557251881Speter  svn_fs_base__deltify,
558251881Speter  svn_fs_base__lock,
559251881Speter  svn_fs_base__generate_lock_token,
560251881Speter  svn_fs_base__unlock,
561251881Speter  svn_fs_base__get_lock,
562251881Speter  svn_fs_base__get_locks,
563299742Sdim  base_bdb_info_format,
564299742Sdim  base_bdb_info_config_files,
565299742Sdim  NULL /* info_fsap */,
566251881Speter  base_bdb_verify_root,
567251881Speter  base_bdb_freeze,
568251881Speter  base_bdb_set_errcall,
569251881Speter};
570251881Speter
571251881Speter/* Where the format number is stored. */
572251881Speter#define FORMAT_FILE   "format"
573251881Speter
574251881Speter/* Depending on CREATE, create or open the environment and databases
575251881Speter   for filesystem FS in PATH. Use POOL for temporary allocations. */
576251881Speterstatic svn_error_t *
577251881Speteropen_databases(svn_fs_t *fs,
578251881Speter               svn_boolean_t create,
579251881Speter               int format,
580251881Speter               const char *path,
581251881Speter               apr_pool_t *pool)
582251881Speter{
583251881Speter  base_fs_data_t *bfd;
584251881Speter
585251881Speter  SVN_ERR(svn_fs__check_fs(fs, FALSE));
586251881Speter
587251881Speter  bfd = apr_pcalloc(fs->pool, sizeof(*bfd));
588251881Speter  fs->vtable = &fs_vtable;
589251881Speter  fs->fsap_data = bfd;
590251881Speter
591251881Speter  /* Initialize the fs's path. */
592251881Speter  fs->path = apr_pstrdup(fs->pool, path);
593251881Speter
594251881Speter  if (create)
595251881Speter    SVN_ERR(bdb_write_config(fs));
596251881Speter
597251881Speter  /* Create the Berkeley DB environment.  */
598251881Speter  {
599251881Speter    svn_error_t *err = svn_fs_bdb__open(&(bfd->bdb), path,
600251881Speter                                        SVN_BDB_STANDARD_ENV_FLAGS,
601251881Speter                                        0666, fs->pool);
602251881Speter    if (err)
603251881Speter      {
604251881Speter        if (create)
605251881Speter          return svn_error_createf
606251881Speter            (err->apr_err, err,
607251881Speter             _("Berkeley DB error for filesystem '%s'"
608251881Speter               " while creating environment:\n"),
609251881Speter             fs->path);
610251881Speter        else
611251881Speter          return svn_error_createf
612251881Speter            (err->apr_err, err,
613251881Speter             _("Berkeley DB error for filesystem '%s'"
614251881Speter               " while opening environment:\n"),
615251881Speter             fs->path);
616251881Speter      }
617251881Speter  }
618251881Speter
619251881Speter  /* We must register the FS cleanup function *after* opening the
620251881Speter     environment, so that it's run before the environment baton
621251881Speter     cleanup. */
622251881Speter  apr_pool_cleanup_register(fs->pool, fs, cleanup_fs_apr,
623251881Speter                            apr_pool_cleanup_null);
624251881Speter
625251881Speter
626251881Speter  /* Create the databases in the environment.  */
627251881Speter  SVN_ERR(BDB_WRAP(fs, (create
628251881Speter                        ? N_("creating 'nodes' table")
629251881Speter                        : N_("opening 'nodes' table")),
630251881Speter                   svn_fs_bdb__open_nodes_table(&bfd->nodes,
631251881Speter                                                bfd->bdb->env,
632251881Speter                                                create)));
633251881Speter  SVN_ERR(BDB_WRAP(fs, (create
634251881Speter                        ? N_("creating 'revisions' table")
635251881Speter                        : N_("opening 'revisions' table")),
636251881Speter                   svn_fs_bdb__open_revisions_table(&bfd->revisions,
637251881Speter                                                    bfd->bdb->env,
638251881Speter                                                    create)));
639251881Speter  SVN_ERR(BDB_WRAP(fs, (create
640251881Speter                        ? N_("creating 'transactions' table")
641251881Speter                        : N_("opening 'transactions' table")),
642251881Speter                   svn_fs_bdb__open_transactions_table(&bfd->transactions,
643251881Speter                                                       bfd->bdb->env,
644251881Speter                                                       create)));
645251881Speter  SVN_ERR(BDB_WRAP(fs, (create
646251881Speter                        ? N_("creating 'copies' table")
647251881Speter                        : N_("opening 'copies' table")),
648251881Speter                   svn_fs_bdb__open_copies_table(&bfd->copies,
649251881Speter                                                 bfd->bdb->env,
650251881Speter                                                 create)));
651251881Speter  SVN_ERR(BDB_WRAP(fs, (create
652251881Speter                        ? N_("creating 'changes' table")
653251881Speter                        : N_("opening 'changes' table")),
654251881Speter                   svn_fs_bdb__open_changes_table(&bfd->changes,
655251881Speter                                                  bfd->bdb->env,
656251881Speter                                                  create)));
657251881Speter  SVN_ERR(BDB_WRAP(fs, (create
658251881Speter                        ? N_("creating 'representations' table")
659251881Speter                        : N_("opening 'representations' table")),
660251881Speter                   svn_fs_bdb__open_reps_table(&bfd->representations,
661251881Speter                                               bfd->bdb->env,
662251881Speter                                               create)));
663251881Speter  SVN_ERR(BDB_WRAP(fs, (create
664251881Speter                        ? N_("creating 'strings' table")
665251881Speter                        : N_("opening 'strings' table")),
666251881Speter                   svn_fs_bdb__open_strings_table(&bfd->strings,
667251881Speter                                                  bfd->bdb->env,
668251881Speter                                                  create)));
669251881Speter  SVN_ERR(BDB_WRAP(fs, (create
670251881Speter                        ? N_("creating 'uuids' table")
671251881Speter                        : N_("opening 'uuids' table")),
672251881Speter                   svn_fs_bdb__open_uuids_table(&bfd->uuids,
673251881Speter                                                bfd->bdb->env,
674251881Speter                                                create)));
675251881Speter  SVN_ERR(BDB_WRAP(fs, (create
676251881Speter                        ? N_("creating 'locks' table")
677251881Speter                        : N_("opening 'locks' table")),
678251881Speter                   svn_fs_bdb__open_locks_table(&bfd->locks,
679251881Speter                                                bfd->bdb->env,
680251881Speter                                                create)));
681251881Speter  SVN_ERR(BDB_WRAP(fs, (create
682251881Speter                        ? N_("creating 'lock-tokens' table")
683251881Speter                        : N_("opening 'lock-tokens' table")),
684251881Speter                   svn_fs_bdb__open_lock_tokens_table(&bfd->lock_tokens,
685251881Speter                                                      bfd->bdb->env,
686251881Speter                                                      create)));
687251881Speter
688251881Speter  if (format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
689251881Speter    {
690251881Speter      SVN_ERR(BDB_WRAP(fs, (create
691251881Speter                            ? N_("creating 'node-origins' table")
692251881Speter                            : N_("opening 'node-origins' table")),
693251881Speter                       svn_fs_bdb__open_node_origins_table(&bfd->node_origins,
694251881Speter                                                           bfd->bdb->env,
695251881Speter                                                           create)));
696251881Speter    }
697251881Speter
698251881Speter  if (format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
699251881Speter    {
700251881Speter      SVN_ERR(BDB_WRAP(fs, (create
701251881Speter                            ? N_("creating 'miscellaneous' table")
702251881Speter                            : N_("opening 'miscellaneous' table")),
703251881Speter                       svn_fs_bdb__open_miscellaneous_table(&bfd->miscellaneous,
704251881Speter                                                            bfd->bdb->env,
705251881Speter                                                            create)));
706251881Speter    }
707251881Speter
708251881Speter  if (format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
709251881Speter    {
710251881Speter      SVN_ERR(BDB_WRAP(fs, (create
711251881Speter                            ? N_("creating 'checksum-reps' table")
712251881Speter                            : N_("opening 'checksum-reps' table")),
713251881Speter                       svn_fs_bdb__open_checksum_reps_table(&bfd->checksum_reps,
714251881Speter                                                            bfd->bdb->env,
715251881Speter                                                            create)));
716251881Speter    }
717251881Speter
718251881Speter  return SVN_NO_ERROR;
719251881Speter}
720251881Speter
721251881Speter
722251881Speter/* Called by functions that initialize an svn_fs_t struct, after that
723251881Speter   initialization is done, to populate svn_fs_t->uuid. */
724251881Speterstatic svn_error_t *
725251881Speterpopulate_opened_fs(svn_fs_t *fs, apr_pool_t *scratch_pool)
726251881Speter{
727251881Speter  SVN_ERR(svn_fs_base__populate_uuid(fs, scratch_pool));
728251881Speter  return SVN_NO_ERROR;
729251881Speter}
730251881Speter
731251881Speterstatic svn_error_t *
732299742Sdimbase_create(svn_fs_t *fs,
733299742Sdim            const char *path,
734299742Sdim            svn_mutex__t *common_pool_lock,
735299742Sdim            apr_pool_t *pool,
736251881Speter            apr_pool_t *common_pool)
737251881Speter{
738251881Speter  int format = SVN_FS_BASE__FORMAT_NUMBER;
739251881Speter  svn_error_t *svn_err;
740251881Speter
741251881Speter  /* See if compatibility with older versions was explicitly requested. */
742251881Speter  if (fs->config)
743251881Speter    {
744299742Sdim      svn_version_t *compatible_version;
745299742Sdim      SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config,
746299742Sdim                                         pool));
747299742Sdim
748299742Sdim      /* select format number */
749299742Sdim      switch(compatible_version->minor)
750299742Sdim        {
751299742Sdim          case 0:
752299742Sdim          case 1:
753299742Sdim          case 2:
754299742Sdim          case 3: format = 1;
755299742Sdim                  break;
756299742Sdim
757299742Sdim          case 4: format = 2;
758299742Sdim                  break;
759299742Sdim
760299742Sdim          case 5: format = 3;
761299742Sdim                  break;
762299742Sdim
763299742Sdim          default:format = SVN_FS_BASE__FORMAT_NUMBER;
764299742Sdim        }
765251881Speter    }
766251881Speter
767251881Speter  /* Create the environment and databases. */
768251881Speter  svn_err = open_databases(fs, TRUE, format, path, pool);
769251881Speter  if (svn_err) goto error;
770251881Speter
771251881Speter  /* Initialize the DAG subsystem. */
772251881Speter  svn_err = svn_fs_base__dag_init_fs(fs);
773251881Speter  if (svn_err) goto error;
774251881Speter
775251881Speter  /* This filesystem is ready.  Stamp it with a format number. */
776251881Speter  svn_err = svn_io_write_version_file(
777251881Speter   svn_dirent_join(fs->path, FORMAT_FILE, pool), format, pool);
778251881Speter  if (svn_err) goto error;
779251881Speter
780251881Speter  ((base_fs_data_t *) fs->fsap_data)->format = format;
781251881Speter
782251881Speter  SVN_ERR(populate_opened_fs(fs, pool));
783251881Speter  return SVN_NO_ERROR;;
784251881Speter
785251881Spetererror:
786299742Sdim  return svn_error_compose_create(svn_err,
787299742Sdim                                  svn_error_trace(cleanup_fs(fs)));
788251881Speter}
789251881Speter
790251881Speter
791251881Speter/* Gaining access to an existing Berkeley DB-based filesystem.  */
792251881Speter
793251881Spetersvn_error_t *
794251881Spetersvn_fs_base__test_required_feature_format(svn_fs_t *fs,
795251881Speter                                          const char *feature,
796251881Speter                                          int requires)
797251881Speter{
798251881Speter  base_fs_data_t *bfd = fs->fsap_data;
799251881Speter  if (bfd->format < requires)
800251881Speter    return svn_error_createf
801251881Speter      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
802251881Speter       _("The '%s' feature requires version %d of the filesystem schema; "
803251881Speter         "filesystem '%s' uses only version %d"),
804251881Speter       feature, requires, fs->path, bfd->format);
805251881Speter  return SVN_NO_ERROR;
806251881Speter}
807251881Speter
808251881Speter/* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
809251881Speter   number is not the same as the format number supported by this
810251881Speter   Subversion. */
811251881Speterstatic svn_error_t *
812251881Spetercheck_format(int format)
813251881Speter{
814251881Speter  /* We currently support any format less than the compiled format number
815251881Speter     simultaneously.  */
816251881Speter  if (format <= SVN_FS_BASE__FORMAT_NUMBER)
817251881Speter    return SVN_NO_ERROR;
818251881Speter
819251881Speter  return svn_error_createf(
820251881Speter        SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
821251881Speter        _("Expected FS format '%d'; found format '%d'"),
822251881Speter        SVN_FS_BASE__FORMAT_NUMBER, format);
823251881Speter}
824251881Speter
825251881Speterstatic svn_error_t *
826299742Sdimbase_open(svn_fs_t *fs,
827299742Sdim          const char *path,
828299742Sdim          svn_mutex__t *common_pool_lock,
829299742Sdim          apr_pool_t *pool,
830251881Speter          apr_pool_t *common_pool)
831251881Speter{
832251881Speter  int format;
833251881Speter  svn_error_t *svn_err;
834251881Speter  svn_boolean_t write_format_file = FALSE;
835251881Speter
836251881Speter  /* Read the FS format number. */
837251881Speter  svn_err = svn_io_read_version_file(&format,
838251881Speter                                     svn_dirent_join(path, FORMAT_FILE, pool),
839251881Speter                                     pool);
840251881Speter  if (svn_err && APR_STATUS_IS_ENOENT(svn_err->apr_err))
841251881Speter    {
842251881Speter      /* Pre-1.2 filesystems did not have a format file (you could say
843251881Speter         they were format "0"), so they get upgraded on the fly.
844251881Speter         However, we stopped "upgrading on the fly" in 1.5, so older
845251881Speter         filesystems should only be bumped to 1.3, which is format "1". */
846251881Speter      svn_error_clear(svn_err);
847251881Speter      svn_err = SVN_NO_ERROR;
848251881Speter      format = 1;
849251881Speter      write_format_file = TRUE;
850251881Speter    }
851251881Speter  else if (svn_err)
852251881Speter    goto error;
853251881Speter
854251881Speter  /* Create the environment and databases. */
855251881Speter  svn_err = open_databases(fs, FALSE, format, path, pool);
856251881Speter  if (svn_err) goto error;
857251881Speter
858251881Speter  ((base_fs_data_t *) fs->fsap_data)->format = format;
859251881Speter  SVN_ERR(check_format(format));
860251881Speter
861251881Speter  /* If we lack a format file, write one. */
862251881Speter  if (write_format_file)
863251881Speter    {
864251881Speter      svn_err = svn_io_write_version_file(svn_dirent_join(path, FORMAT_FILE,
865251881Speter                                                        pool),
866251881Speter                                          format, pool);
867251881Speter      if (svn_err) goto error;
868251881Speter    }
869251881Speter
870251881Speter  SVN_ERR(populate_opened_fs(fs, pool));
871251881Speter  return SVN_NO_ERROR;
872251881Speter
873251881Speter error:
874299742Sdim  return svn_error_compose_create(svn_err,
875299742Sdim                                  svn_error_trace(cleanup_fs(fs)));
876251881Speter}
877251881Speter
878251881Speter
879251881Speter/* Running recovery on a Berkeley DB-based filesystem.  */
880251881Speter
881251881Speter
882251881Speter/* Recover a database at PATH. Perform catastrophic recovery if FATAL
883251881Speter   is TRUE. Use POOL for temporary allocation. */
884251881Speterstatic svn_error_t *
885251881Speterbdb_recover(const char *path, svn_boolean_t fatal, apr_pool_t *pool)
886251881Speter{
887251881Speter  bdb_env_baton_t *bdb;
888251881Speter
889251881Speter  /* Here's the comment copied from db_recover.c:
890251881Speter
891251881Speter     Initialize the environment -- we don't actually do anything
892251881Speter     else, that all that's needed to run recovery.
893251881Speter
894251881Speter     Note that we specify a private environment, as we're about to
895251881Speter     create a region, and we don't want to leave it around.  If we
896251881Speter     leave the region around, the application that should create it
897251881Speter     will simply join it instead, and will then be running with
898251881Speter     incorrectly sized (and probably terribly small) caches.  */
899251881Speter
900251881Speter  /* Note that since we're using a private environment, we shoudl
901251881Speter     /not/ initialize locking. We want the environment files to go
902251881Speter     away. */
903251881Speter
904251881Speter  SVN_ERR(svn_fs_bdb__open(&bdb, path,
905251881Speter                           ((fatal ? DB_RECOVER_FATAL : DB_RECOVER)
906251881Speter                            | SVN_BDB_PRIVATE_ENV_FLAGS),
907251881Speter                           0666, pool));
908251881Speter  return svn_fs_bdb__close(bdb);
909251881Speter}
910251881Speter
911251881Speterstatic svn_error_t *
912299742Sdimbase_open_for_recovery(svn_fs_t *fs,
913299742Sdim                       const char *path,
914299742Sdim                       svn_mutex__t *common_pool_lock,
915299742Sdim                       apr_pool_t *pool,
916251881Speter                       apr_pool_t *common_pool)
917251881Speter{
918251881Speter  /* Just stash the path in the fs pointer - it's all we really need. */
919251881Speter  fs->path = apr_pstrdup(fs->pool, path);
920251881Speter
921251881Speter  return SVN_NO_ERROR;
922251881Speter}
923251881Speter
924251881Speterstatic svn_error_t *
925299742Sdimbase_upgrade(svn_fs_t *fs,
926299742Sdim             const char *path,
927299742Sdim             svn_fs_upgrade_notify_t notify_func,
928299742Sdim             void *notify_baton,
929299742Sdim             svn_cancel_func_t cancel_func,
930299742Sdim             void *cancel_baton,
931299742Sdim             svn_mutex__t *common_pool_lock,
932299742Sdim             apr_pool_t *pool,
933251881Speter             apr_pool_t *common_pool)
934251881Speter{
935251881Speter  const char *version_file_path;
936251881Speter  int old_format_number;
937251881Speter  svn_error_t *err;
938251881Speter
939251881Speter  version_file_path = svn_dirent_join(path, FORMAT_FILE, pool);
940251881Speter
941251881Speter  /* Read the old number so we've got it on hand later on. */
942251881Speter  err = svn_io_read_version_file(&old_format_number, version_file_path, pool);
943251881Speter  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
944251881Speter    {
945251881Speter      /* Pre-1.2 filesystems do not have a 'format' file. */
946251881Speter      old_format_number = 0;
947251881Speter      svn_error_clear(err);
948251881Speter      err = SVN_NO_ERROR;
949251881Speter    }
950251881Speter  SVN_ERR(err);
951251881Speter
952251881Speter  /* Bump the format file's stored version number. */
953251881Speter  SVN_ERR(svn_io_write_version_file(version_file_path,
954251881Speter                                    SVN_FS_BASE__FORMAT_NUMBER, pool));
955299742Sdim  if (notify_func)
956299742Sdim    SVN_ERR(notify_func(notify_baton, SVN_FS_BASE__FORMAT_NUMBER,
957299742Sdim                        svn_fs_upgrade_format_bumped, pool));
958251881Speter
959251881Speter  /* Check and see if we need to record the "bump" revision. */
960251881Speter  if (old_format_number < SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT)
961251881Speter    {
962251881Speter      apr_pool_t *subpool = svn_pool_create(pool);
963251881Speter      svn_revnum_t youngest_rev;
964251881Speter      const char *value;
965251881Speter
966251881Speter      /* Open the filesystem in a subpool (so we can control its
967251881Speter         closure) and do our fiddling.
968251881Speter
969251881Speter         NOTE: By using base_open() here instead of open_databases(),
970251881Speter         we will end up re-reading the format file that we just wrote.
971251881Speter         But it's better to use the existing encapsulation of "opening
972251881Speter         the filesystem" rather than duplicating (or worse, partially
973251881Speter         duplicating) that logic here.  */
974299742Sdim      SVN_ERR(base_open(fs, path, common_pool_lock, subpool, common_pool));
975251881Speter
976251881Speter      /* Fetch the youngest rev, and record it */
977251881Speter      SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, subpool));
978251881Speter      value = apr_psprintf(subpool, "%ld", youngest_rev);
979251881Speter      SVN_ERR(svn_fs_base__miscellaneous_set
980251881Speter              (fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE,
981251881Speter               value, subpool));
982251881Speter      svn_pool_destroy(subpool);
983251881Speter    }
984251881Speter
985251881Speter  return SVN_NO_ERROR;
986251881Speter}
987251881Speter
988251881Speterstatic svn_error_t *
989251881Speterbase_verify(svn_fs_t *fs, const char *path,
990251881Speter            svn_revnum_t start,
991251881Speter            svn_revnum_t end,
992251881Speter            svn_fs_progress_notify_func_t notify_func,
993251881Speter            void *notify_baton,
994251881Speter            svn_cancel_func_t cancel_func,
995251881Speter            void *cancel_baton,
996299742Sdim            svn_mutex__t *common_pool_lock,
997251881Speter            apr_pool_t *pool,
998251881Speter            apr_pool_t *common_pool)
999251881Speter{
1000251881Speter  /* Verifying is currently a no op for BDB. */
1001251881Speter  return SVN_NO_ERROR;
1002251881Speter}
1003251881Speter
1004251881Speterstatic svn_error_t *
1005251881Speterbase_bdb_recover(svn_fs_t *fs,
1006251881Speter                 svn_cancel_func_t cancel_func, void *cancel_baton,
1007251881Speter                 apr_pool_t *pool)
1008251881Speter{
1009251881Speter  /* The fs pointer is a fake created in base_open_for_recovery above.
1010251881Speter     We only care about the path. */
1011251881Speter  return bdb_recover(fs->path, FALSE, pool);
1012251881Speter}
1013251881Speter
1014251881Speterstatic svn_error_t *
1015251881Speterbase_bdb_pack(svn_fs_t *fs,
1016251881Speter              const char *path,
1017251881Speter              svn_fs_pack_notify_t notify_func,
1018251881Speter              void *notify_baton,
1019251881Speter              svn_cancel_func_t cancel,
1020251881Speter              void *cancel_baton,
1021299742Sdim              svn_mutex__t *common_pool_lock,
1022251881Speter              apr_pool_t *pool,
1023251881Speter              apr_pool_t *common_pool)
1024251881Speter{
1025251881Speter  /* Packing is currently a no op for BDB. */
1026251881Speter  return SVN_NO_ERROR;
1027251881Speter}
1028251881Speter
1029251881Speter
1030251881Speter
1031251881Speter/* Running the 'archive' command on a Berkeley DB-based filesystem.  */
1032251881Speter
1033251881Speter
1034251881Speterstatic svn_error_t *
1035251881Speterbase_bdb_logfiles(apr_array_header_t **logfiles,
1036251881Speter                  const char *path,
1037251881Speter                  svn_boolean_t only_unused,
1038251881Speter                  apr_pool_t *pool)
1039251881Speter{
1040251881Speter  bdb_env_baton_t *bdb;
1041251881Speter  char **filelist;
1042251881Speter  char **filename;
1043251881Speter  u_int32_t flags = only_unused ? 0 : DB_ARCH_LOG;
1044251881Speter
1045251881Speter  *logfiles = apr_array_make(pool, 4, sizeof(const char *));
1046251881Speter
1047251881Speter  SVN_ERR(svn_fs_bdb__open(&bdb, path,
1048251881Speter                           SVN_BDB_STANDARD_ENV_FLAGS,
1049251881Speter                           0666, pool));
1050251881Speter  SVN_BDB_ERR(bdb, bdb->env->log_archive(bdb->env, &filelist, flags));
1051251881Speter
1052251881Speter  if (filelist == NULL)
1053251881Speter    return svn_fs_bdb__close(bdb);
1054251881Speter
1055251881Speter  for (filename = filelist; *filename != NULL; ++filename)
1056251881Speter    {
1057251881Speter      APR_ARRAY_PUSH(*logfiles, const char *) = apr_pstrdup(pool, *filename);
1058251881Speter    }
1059251881Speter
1060251881Speter  free(filelist);
1061251881Speter
1062251881Speter  return svn_fs_bdb__close(bdb);
1063251881Speter}
1064251881Speter
1065251881Speter
1066251881Speter
1067251881Speter/* Copying a live Berkeley DB-base filesystem.  */
1068251881Speter
1069251881Speter/**
1070251881Speter * Delete all unused log files from DBD enviroment at @a live_path that exist
1071251881Speter * in @a backup_path.
1072251881Speter */
1073251881Speterstatic svn_error_t *
1074251881Spetersvn_fs_base__clean_logs(const char *live_path,
1075251881Speter                        const char *backup_path,
1076251881Speter                        apr_pool_t *pool)
1077251881Speter{
1078251881Speter  apr_array_header_t *logfiles;
1079251881Speter
1080251881Speter  SVN_ERR(base_bdb_logfiles(&logfiles,
1081251881Speter                            live_path,
1082251881Speter                            TRUE,        /* Only unused logs */
1083251881Speter                            pool));
1084251881Speter
1085251881Speter  {  /* Process unused logs from live area */
1086251881Speter    int idx;
1087299742Sdim    apr_pool_t *subpool = svn_pool_create(pool);
1088251881Speter
1089251881Speter    /* Process log files. */
1090251881Speter    for (idx = 0; idx < logfiles->nelts; idx++)
1091251881Speter      {
1092251881Speter        const char *log_file = APR_ARRAY_IDX(logfiles, idx, const char *);
1093251881Speter        const char *live_log_path;
1094251881Speter        const char *backup_log_path;
1095251881Speter
1096299742Sdim        svn_pool_clear(subpool);
1097299742Sdim        live_log_path = svn_dirent_join(live_path, log_file, subpool);
1098299742Sdim        backup_log_path = svn_dirent_join(backup_path, log_file, subpool);
1099251881Speter
1100251881Speter        { /* Compare files. No point in using MD5 and wasting CPU cycles as we
1101251881Speter             got full copies of both logs */
1102251881Speter
1103251881Speter          svn_boolean_t files_match = FALSE;
1104251881Speter          svn_node_kind_t kind;
1105251881Speter
1106251881Speter          /* Check to see if there is a corresponding log file in the backup
1107251881Speter             directory */
1108251881Speter          SVN_ERR(svn_io_check_path(backup_log_path, &kind, pool));
1109251881Speter
1110251881Speter          /* If the copy of the log exists, compare them */
1111251881Speter          if (kind == svn_node_file)
1112251881Speter            SVN_ERR(svn_io_files_contents_same_p(&files_match,
1113251881Speter                                                 live_log_path,
1114251881Speter                                                 backup_log_path,
1115299742Sdim                                                 subpool));
1116251881Speter
1117251881Speter          /* If log files do not match, go to the next log file. */
1118251881Speter          if (!files_match)
1119251881Speter            continue;
1120251881Speter        }
1121251881Speter
1122299742Sdim        SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, subpool));
1123251881Speter      }
1124251881Speter
1125299742Sdim    svn_pool_destroy(subpool);
1126251881Speter  }
1127251881Speter
1128251881Speter  return SVN_NO_ERROR;
1129251881Speter}
1130251881Speter
1131251881Speter
1132251881Speter/* DB_ENV->get_flags() and DB->get_pagesize() don't exist prior to
1133251881Speter   Berkeley DB 4.2. */
1134251881Speter#if SVN_BDB_VERSION_AT_LEAST(4, 2)
1135251881Speter
1136251881Speter/* Open the BDB environment at PATH and compare its configuration
1137251881Speter   flags with FLAGS.  If every flag in FLAGS is set in the
1138251881Speter   environment, then set *MATCH to true.  Else set *MATCH to false. */
1139251881Speterstatic svn_error_t *
1140251881Spetercheck_env_flags(svn_boolean_t *match,
1141251881Speter                u_int32_t flags,
1142251881Speter                const char *path,
1143251881Speter                apr_pool_t *pool)
1144251881Speter{
1145251881Speter  bdb_env_baton_t *bdb;
1146251881Speter#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1147251881Speter  int flag_state;
1148251881Speter#else
1149251881Speter  u_int32_t envflags;
1150251881Speter#endif
1151251881Speter
1152251881Speter  SVN_ERR(svn_fs_bdb__open(&bdb, path,
1153251881Speter                           SVN_BDB_STANDARD_ENV_FLAGS,
1154251881Speter                           0666, pool));
1155251881Speter#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1156251881Speter  SVN_BDB_ERR(bdb, bdb->env->log_get_config(bdb->env, flags, &flag_state));
1157251881Speter#else
1158251881Speter  SVN_BDB_ERR(bdb, bdb->env->get_flags(bdb->env, &envflags));
1159251881Speter#endif
1160251881Speter
1161251881Speter  SVN_ERR(svn_fs_bdb__close(bdb));
1162251881Speter
1163251881Speter#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1164251881Speter  if (flag_state == 0)
1165251881Speter#else
1166251881Speter  if (flags & envflags)
1167251881Speter#endif
1168251881Speter    *match = TRUE;
1169251881Speter  else
1170251881Speter    *match = FALSE;
1171251881Speter
1172251881Speter  return SVN_NO_ERROR;
1173251881Speter}
1174251881Speter
1175251881Speter
1176251881Speter/* Set *PAGESIZE to the size of pages used to hold items in the
1177251881Speter   database environment located at PATH.
1178251881Speter*/
1179251881Speterstatic svn_error_t *
1180251881Speterget_db_pagesize(u_int32_t *pagesize,
1181251881Speter                const char *path,
1182251881Speter                apr_pool_t *pool)
1183251881Speter{
1184251881Speter  bdb_env_baton_t *bdb;
1185251881Speter  DB *nodes_table;
1186251881Speter
1187251881Speter  SVN_ERR(svn_fs_bdb__open(&bdb, path,
1188251881Speter                           SVN_BDB_STANDARD_ENV_FLAGS,
1189251881Speter                           0666, pool));
1190251881Speter
1191251881Speter  /* ### We're only asking for the pagesize on the 'nodes' table.
1192251881Speter         Is this enough?  We never call DB->set_pagesize() on any of
1193251881Speter         our tables, so presumably BDB is using the same default
1194251881Speter         pagesize for all our databases, right? */
1195251881Speter  SVN_BDB_ERR(bdb, svn_fs_bdb__open_nodes_table(&nodes_table, bdb->env,
1196251881Speter                                                FALSE));
1197251881Speter  SVN_BDB_ERR(bdb, nodes_table->get_pagesize(nodes_table, pagesize));
1198251881Speter  SVN_BDB_ERR(bdb, nodes_table->close(nodes_table, 0));
1199251881Speter
1200251881Speter  return svn_fs_bdb__close(bdb);
1201251881Speter}
1202251881Speter#endif /* SVN_BDB_VERSION_AT_LEAST(4, 2) */
1203251881Speter
1204251881Speter
1205251881Speter/* Copy FILENAME from SRC_DIR to DST_DIR in byte increments of size
1206251881Speter   CHUNKSIZE.  The read/write buffer of size CHUNKSIZE will be
1207251881Speter   allocated in POOL.  If ALLOW_MISSING is set, we won't make a fuss
1208251881Speter   if FILENAME isn't found in SRC_DIR; otherwise, we will.  */
1209251881Speterstatic svn_error_t *
1210251881Spetercopy_db_file_safely(const char *src_dir,
1211251881Speter                    const char *dst_dir,
1212251881Speter                    const char *filename,
1213251881Speter                    u_int32_t chunksize,
1214251881Speter                    svn_boolean_t allow_missing,
1215251881Speter                    apr_pool_t *pool)
1216251881Speter{
1217251881Speter  apr_file_t *s = NULL, *d = NULL;  /* init to null important for APR */
1218251881Speter  const char *file_src_path = svn_dirent_join(src_dir, filename, pool);
1219251881Speter  const char *file_dst_path = svn_dirent_join(dst_dir, filename, pool);
1220251881Speter  svn_error_t *err;
1221251881Speter  char *buf;
1222251881Speter
1223251881Speter  /* Open source file.  If it's missing and that's allowed, there's
1224251881Speter     nothing more to do here. */
1225251881Speter  err = svn_io_file_open(&s, file_src_path,
1226251881Speter                         (APR_READ | APR_LARGEFILE),
1227251881Speter                         APR_OS_DEFAULT, pool);
1228251881Speter  if (err && APR_STATUS_IS_ENOENT(err->apr_err) && allow_missing)
1229251881Speter    {
1230251881Speter      svn_error_clear(err);
1231251881Speter      return SVN_NO_ERROR;
1232251881Speter    }
1233251881Speter  SVN_ERR(err);
1234251881Speter
1235251881Speter  /* Open destination file. */
1236251881Speter  SVN_ERR(svn_io_file_open(&d, file_dst_path, (APR_WRITE | APR_CREATE |
1237251881Speter                                               APR_LARGEFILE),
1238251881Speter                           APR_OS_DEFAULT, pool));
1239251881Speter
1240251881Speter  /* Allocate our read/write buffer. */
1241251881Speter  buf = apr_palloc(pool, chunksize);
1242251881Speter
1243251881Speter  /* Copy bytes till the cows come home. */
1244251881Speter  while (1)
1245251881Speter    {
1246251881Speter      apr_size_t bytes_this_time = chunksize;
1247251881Speter      svn_error_t *read_err, *write_err;
1248251881Speter
1249251881Speter      /* Read 'em. */
1250251881Speter      if ((read_err = svn_io_file_read(s, buf, &bytes_this_time, pool)))
1251251881Speter        {
1252251881Speter          if (APR_STATUS_IS_EOF(read_err->apr_err))
1253251881Speter            svn_error_clear(read_err);
1254251881Speter          else
1255251881Speter            {
1256251881Speter              svn_error_clear(svn_io_file_close(s, pool));
1257251881Speter              svn_error_clear(svn_io_file_close(d, pool));
1258251881Speter              return read_err;
1259251881Speter            }
1260251881Speter        }
1261251881Speter
1262251881Speter      /* Write 'em. */
1263251881Speter      if ((write_err = svn_io_file_write_full(d, buf, bytes_this_time, NULL,
1264251881Speter                                              pool)))
1265251881Speter        {
1266251881Speter          svn_error_clear(svn_io_file_close(s, pool));
1267251881Speter          svn_error_clear(svn_io_file_close(d, pool));
1268251881Speter          return write_err;
1269251881Speter        }
1270251881Speter
1271251881Speter      /* read_err is either NULL, or a dangling pointer - but it is only a
1272251881Speter         dangling pointer if it used to be an EOF error. */
1273251881Speter      if (read_err)
1274251881Speter        {
1275251881Speter          SVN_ERR(svn_io_file_close(s, pool));
1276251881Speter          SVN_ERR(svn_io_file_close(d, pool));
1277251881Speter          break;  /* got EOF on read, all files closed, all done. */
1278251881Speter        }
1279251881Speter    }
1280251881Speter
1281251881Speter  return SVN_NO_ERROR;
1282251881Speter}
1283251881Speter
1284251881Speter
1285251881Speter
1286251881Speter
1287251881Speterstatic svn_error_t *
1288251881Speterbase_hotcopy(svn_fs_t *src_fs,
1289251881Speter             svn_fs_t *dst_fs,
1290251881Speter             const char *src_path,
1291251881Speter             const char *dest_path,
1292251881Speter             svn_boolean_t clean_logs,
1293251881Speter             svn_boolean_t incremental,
1294299742Sdim             svn_fs_hotcopy_notify_t notify_func,
1295299742Sdim             void *notify_baton,
1296251881Speter             svn_cancel_func_t cancel_func,
1297251881Speter             void *cancel_baton,
1298299742Sdim             svn_mutex__t *common_pool_lock,
1299299742Sdim             apr_pool_t *pool,
1300299742Sdim             apr_pool_t *common_pool)
1301251881Speter{
1302251881Speter  svn_error_t *err;
1303251881Speter  u_int32_t pagesize;
1304251881Speter  svn_boolean_t log_autoremove = FALSE;
1305251881Speter  int format;
1306251881Speter
1307251881Speter  if (incremental)
1308251881Speter    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1309251881Speter                             _("BDB repositories do not support incremental "
1310251881Speter                               "hotcopy"));
1311251881Speter
1312251881Speter  /* Check the FS format number to be certain that we know how to
1313251881Speter     hotcopy this FS.  Pre-1.2 filesystems did not have a format file (you
1314251881Speter     could say they were format "0"), so we will error here.  This is not
1315251881Speter     optimal, but since this has been the case since 1.2.0, and no one has
1316251881Speter     complained, it apparently isn't much of a concern.  (We did not check
1317251881Speter     the 'format' file in 1.2.x, but we did blindly try to copy 'locks',
1318251881Speter     which would have errored just the same.)  */
1319251881Speter  SVN_ERR(svn_io_read_version_file(
1320251881Speter          &format, svn_dirent_join(src_path, FORMAT_FILE, pool), pool));
1321251881Speter  SVN_ERR(check_format(format));
1322251881Speter
1323251881Speter  /* If using Berkeley DB 4.2 or later, note whether the DB_LOG_AUTO_REMOVE
1324251881Speter     feature is on.  If it is, we have a potential race condition:
1325251881Speter     another process might delete a logfile while we're in the middle
1326251881Speter     of copying all the logfiles.  (This is not a huge deal; at worst,
1327251881Speter     the hotcopy fails with a file-not-found error.) */
1328251881Speter#if SVN_BDB_VERSION_AT_LEAST(4, 2)
1329251881Speter  err = check_env_flags(&log_autoremove,
1330251881Speter#if SVN_BDB_VERSION_AT_LEAST(4, 7)
1331251881Speter                          DB_LOG_AUTO_REMOVE,
1332251881Speter /* DB_LOG_AUTO_REMOVE was named DB_LOG_AUTOREMOVE before Berkeley DB 4.7. */
1333251881Speter#else
1334251881Speter                          DB_LOG_AUTOREMOVE,
1335251881Speter#endif
1336251881Speter                          src_path, pool);
1337251881Speter#endif
1338251881Speter  SVN_ERR(err);
1339251881Speter
1340251881Speter  /* Copy the DB_CONFIG file. */
1341251881Speter  SVN_ERR(svn_io_dir_file_copy(src_path, dest_path, "DB_CONFIG", pool));
1342251881Speter
1343251881Speter  /* In order to copy the database files safely and atomically, we
1344251881Speter     must copy them in chunks which are multiples of the page-size
1345251881Speter     used by BDB.  See sleepycat docs for details, or svn issue #1818. */
1346251881Speter#if SVN_BDB_VERSION_AT_LEAST(4, 2)
1347251881Speter  SVN_ERR(get_db_pagesize(&pagesize, src_path, pool));
1348251881Speter  if (pagesize < SVN__STREAM_CHUNK_SIZE)
1349251881Speter    {
1350251881Speter      /* use the largest multiple of BDB pagesize we can. */
1351251881Speter      int multiple = SVN__STREAM_CHUNK_SIZE / pagesize;
1352251881Speter      pagesize *= multiple;
1353251881Speter    }
1354251881Speter#else
1355251881Speter  /* default to 128K chunks, which should be safe.
1356251881Speter     BDB almost certainly uses a power-of-2 pagesize. */
1357251881Speter  pagesize = (4096 * 32);
1358251881Speter#endif
1359251881Speter
1360251881Speter  /* Copy the databases.  */
1361251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1362251881Speter                              "nodes", pagesize, FALSE, pool));
1363251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1364251881Speter                              "transactions", pagesize, FALSE, pool));
1365251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1366251881Speter                              "revisions", pagesize, FALSE, pool));
1367251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1368251881Speter                              "copies", pagesize, FALSE, pool));
1369251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1370251881Speter                              "changes", pagesize, FALSE, pool));
1371251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1372251881Speter                              "representations", pagesize, FALSE, pool));
1373251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1374251881Speter                              "strings", pagesize, FALSE, pool));
1375251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1376251881Speter                              "uuids", pagesize, TRUE, pool));
1377251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1378251881Speter                              "locks", pagesize, TRUE, pool));
1379251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1380251881Speter                              "lock-tokens", pagesize, TRUE, pool));
1381251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1382251881Speter                              "node-origins", pagesize, TRUE, pool));
1383251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1384251881Speter                              "checksum-reps", pagesize, TRUE, pool));
1385251881Speter  SVN_ERR(copy_db_file_safely(src_path, dest_path,
1386251881Speter                              "miscellaneous", pagesize, TRUE, pool));
1387251881Speter
1388251881Speter  {
1389251881Speter    apr_array_header_t *logfiles;
1390251881Speter    int idx;
1391251881Speter    apr_pool_t *subpool;
1392251881Speter
1393251881Speter    SVN_ERR(base_bdb_logfiles(&logfiles,
1394251881Speter                              src_path,
1395251881Speter                              FALSE,   /* All logs */
1396251881Speter                              pool));
1397251881Speter
1398251881Speter    /* Process log files. */
1399251881Speter    subpool = svn_pool_create(pool);
1400251881Speter    for (idx = 0; idx < logfiles->nelts; idx++)
1401251881Speter      {
1402251881Speter        svn_pool_clear(subpool);
1403251881Speter        err = svn_io_dir_file_copy(src_path, dest_path,
1404251881Speter                                   APR_ARRAY_IDX(logfiles, idx,
1405251881Speter                                                 const char *),
1406251881Speter                                   subpool);
1407251881Speter        if (err)
1408251881Speter          {
1409251881Speter            if (log_autoremove)
1410251881Speter              return
1411251881Speter                svn_error_quick_wrap
1412251881Speter                (err,
1413251881Speter                 _("Error copying logfile;  the DB_LOG_AUTOREMOVE feature\n"
1414251881Speter                   "may be interfering with the hotcopy algorithm.  If\n"
1415251881Speter                   "the problem persists, try deactivating this feature\n"
1416251881Speter                   "in DB_CONFIG"));
1417251881Speter            else
1418251881Speter              return svn_error_trace(err);
1419251881Speter          }
1420251881Speter      }
1421251881Speter    svn_pool_destroy(subpool);
1422251881Speter  }
1423251881Speter
1424251881Speter  /* Since this is a copy we will have exclusive access to the repository. */
1425251881Speter  err = bdb_recover(dest_path, TRUE, pool);
1426251881Speter  if (err)
1427251881Speter    {
1428251881Speter      if (log_autoremove)
1429251881Speter        return
1430251881Speter          svn_error_quick_wrap
1431251881Speter          (err,
1432251881Speter           _("Error running catastrophic recovery on hotcopy;  the\n"
1433251881Speter             "DB_LOG_AUTOREMOVE feature may be interfering with the\n"
1434251881Speter             "hotcopy algorithm.  If the problem persists, try deactivating\n"
1435251881Speter             "this feature in DB_CONFIG"));
1436251881Speter      else
1437251881Speter        return svn_error_trace(err);
1438251881Speter    }
1439251881Speter
1440251881Speter  /* Only now that the hotcopied filesystem is complete,
1441251881Speter     stamp it with a format file. */
1442251881Speter  SVN_ERR(svn_io_write_version_file(
1443251881Speter             svn_dirent_join(dest_path, FORMAT_FILE, pool), format, pool));
1444251881Speter
1445251881Speter  if (clean_logs)
1446251881Speter    SVN_ERR(svn_fs_base__clean_logs(src_path, dest_path, pool));
1447251881Speter
1448251881Speter  return SVN_NO_ERROR;
1449251881Speter}
1450251881Speter
1451251881Speter
1452251881Speter
1453251881Speter/* Deleting a Berkeley DB-based filesystem.  */
1454251881Speter
1455251881Speter
1456251881Speterstatic svn_error_t *
1457251881Speterbase_delete_fs(const char *path,
1458251881Speter               apr_pool_t *pool)
1459251881Speter{
1460251881Speter  /* First, use the Berkeley DB library function to remove any shared
1461251881Speter     memory segments.  */
1462251881Speter  SVN_ERR(svn_fs_bdb__remove(path, pool));
1463251881Speter
1464251881Speter  /* Remove the environment directory. */
1465251881Speter  return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
1466251881Speter}
1467251881Speter
1468251881Speterstatic const svn_version_t *
1469251881Speterbase_version(void)
1470251881Speter{
1471251881Speter  SVN_VERSION_BODY;
1472251881Speter}
1473251881Speter
1474251881Speterstatic const char *
1475251881Speterbase_get_description(void)
1476251881Speter{
1477251881Speter  return _("Module for working with a Berkeley DB repository.");
1478251881Speter}
1479251881Speter
1480251881Speterstatic svn_error_t *
1481251881Speterbase_set_svn_fs_open(svn_fs_t *fs,
1482251881Speter                     svn_error_t *(*svn_fs_open_)(svn_fs_t **,
1483251881Speter                                                  const char *,
1484251881Speter                                                  apr_hash_t *,
1485299742Sdim                                                  apr_pool_t *,
1486251881Speter                                                  apr_pool_t *))
1487251881Speter{
1488251881Speter  return SVN_NO_ERROR;
1489251881Speter}
1490251881Speter
1491251881Speter
1492251881Speter/* Base FS library vtable, used by the FS loader library. */
1493251881Speterstatic fs_library_vtable_t library_vtable = {
1494251881Speter  base_version,
1495251881Speter  base_create,
1496251881Speter  base_open,
1497251881Speter  base_open_for_recovery,
1498251881Speter  base_upgrade,
1499251881Speter  base_verify,
1500251881Speter  base_delete_fs,
1501251881Speter  base_hotcopy,
1502251881Speter  base_get_description,
1503251881Speter  base_bdb_recover,
1504251881Speter  base_bdb_pack,
1505251881Speter  base_bdb_logfiles,
1506251881Speter  svn_fs_base__id_parse,
1507299742Sdim  base_set_svn_fs_open,
1508299742Sdim  NULL /* info_fsap_dup */
1509251881Speter};
1510251881Speter
1511251881Spetersvn_error_t *
1512251881Spetersvn_fs_base__init(const svn_version_t *loader_version,
1513251881Speter                  fs_library_vtable_t **vtable, apr_pool_t* common_pool)
1514251881Speter{
1515251881Speter  static const svn_version_checklist_t checklist[] =
1516251881Speter    {
1517251881Speter      { "svn_subr",  svn_subr_version },
1518251881Speter      { "svn_delta", svn_delta_version },
1519299742Sdim      { "svn_fs_util", svn_fs_util__version },
1520251881Speter      { NULL, NULL }
1521251881Speter    };
1522251881Speter
1523251881Speter  /* Simplified version check to make sure we can safely use the
1524251881Speter     VTABLE parameter. The FS loader does a more exhaustive check. */
1525251881Speter  if (loader_version->major != SVN_VER_MAJOR)
1526251881Speter    return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
1527251881Speter                             _("Unsupported FS loader version (%d) for bdb"),
1528251881Speter                             loader_version->major);
1529262253Speter  SVN_ERR(svn_ver_check_list2(base_version(), checklist, svn_ver_equal));
1530251881Speter  SVN_ERR(check_bdb_version());
1531251881Speter  SVN_ERR(svn_fs_bdb__init(common_pool));
1532251881Speter
1533251881Speter  *vtable = &library_vtable;
1534251881Speter  return SVN_NO_ERROR;
1535251881Speter}
1536