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