1 /* fs.c --- creating, opening and closing filesystems
2 *
3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 * ====================================================================
21 */
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <apr_general.h>
28 #include <apr_pools.h>
29 #include <apr_file_io.h>
30
31 #include "svn_hash.h"
32 #include "svn_pools.h"
33 #include "svn_fs.h"
34 #include "svn_path.h"
35 #include "svn_utf.h"
36 #include "svn_delta.h"
37 #include "svn_version.h"
38 #include "fs.h"
39 #include "err.h"
40 #include "dag.h"
41 #include "revs-txns.h"
42 #include "uuid.h"
43 #include "tree.h"
44 #include "id.h"
45 #include "lock.h"
46 #define SVN_WANT_BDB
47 #include "svn_private_config.h"
48
49 #include "bdb/bdb-err.h"
50 #include "bdb/bdb_compat.h"
51 #include "bdb/env.h"
52 #include "bdb/nodes-table.h"
53 #include "bdb/rev-table.h"
54 #include "bdb/txn-table.h"
55 #include "bdb/copies-table.h"
56 #include "bdb/changes-table.h"
57 #include "bdb/reps-table.h"
58 #include "bdb/strings-table.h"
59 #include "bdb/uuids-table.h"
60 #include "bdb/locks-table.h"
61 #include "bdb/lock-tokens-table.h"
62 #include "bdb/node-origins-table.h"
63 #include "bdb/miscellaneous-table.h"
64 #include "bdb/checksum-reps-table.h"
65
66 #include "../libsvn_fs/fs-loader.h"
67 #include "private/svn_fs_util.h"
68
69
70 /* Checking for return values, and reporting errors. */
71
72 /* Check that we're using the right Berkeley DB version. */
73 /* FIXME: This check should be abstracted into the DB back-end layer. */
74 static svn_error_t *
check_bdb_version(void)75 check_bdb_version(void)
76 {
77 int major, minor, patch;
78
79 db_version(&major, &minor, &patch);
80
81 /* First, check that we're using a reasonably correct of Berkeley DB. */
82 if ((major < SVN_FS_WANT_DB_MAJOR)
83 || (major == SVN_FS_WANT_DB_MAJOR && minor < SVN_FS_WANT_DB_MINOR)
84 || (major == SVN_FS_WANT_DB_MAJOR && minor == SVN_FS_WANT_DB_MINOR
85 && patch < SVN_FS_WANT_DB_PATCH))
86 return svn_error_createf(SVN_ERR_FS_GENERAL, 0,
87 _("Bad database version: got %d.%d.%d,"
88 " should be at least %d.%d.%d"),
89 major, minor, patch,
90 SVN_FS_WANT_DB_MAJOR,
91 SVN_FS_WANT_DB_MINOR,
92 SVN_FS_WANT_DB_PATCH);
93
94 /* Now, check that the version we're running against is the same as
95 the one we compiled with. */
96 if (major != DB_VERSION_MAJOR || minor != DB_VERSION_MINOR)
97 return svn_error_createf(SVN_ERR_FS_GENERAL, 0,
98 _("Bad database version:"
99 " compiled with %d.%d.%d,"
100 " running against %d.%d.%d"),
101 DB_VERSION_MAJOR,
102 DB_VERSION_MINOR,
103 DB_VERSION_PATCH,
104 major, minor, patch);
105 return SVN_NO_ERROR;
106 }
107
108
109
110 /* Cleanup functions. */
111
112 /* Close a database in the filesystem FS.
113 DB_PTR is a pointer to the DB pointer in *FS to close.
114 NAME is the name of the database, for use in error messages. */
115 static svn_error_t *
cleanup_fs_db(svn_fs_t * fs,DB ** db_ptr,const char * name)116 cleanup_fs_db(svn_fs_t *fs, DB **db_ptr, const char *name)
117 {
118 /* If the BDB environment is panicked, don't do anything, since
119 attempting to close the database will fail anyway. */
120 base_fs_data_t *bfd = fs->fsap_data;
121 if (*db_ptr && !svn_fs_bdb__get_panic(bfd->bdb))
122 {
123 DB *db = *db_ptr;
124 char *msg = apr_psprintf(fs->pool, "closing '%s' database", name);
125 int db_err;
126
127 *db_ptr = 0;
128 db_err = db->close(db, 0);
129 if (db_err == DB_RUNRECOVERY)
130 {
131 /* We can ignore DB_RUNRECOVERY errors from DB->close, but
132 must set the panic flag in the environment baton. The
133 error will be propagated appropriately from
134 svn_fs_bdb__close. */
135 svn_fs_bdb__set_panic(bfd->bdb);
136 db_err = 0;
137 }
138
139 #if SVN_BDB_HAS_DB_INCOMPLETE
140 /* We can ignore DB_INCOMPLETE on db->close and db->sync; it
141 * just means someone else was using the db at the same time
142 * we were. See the Berkeley documentation at:
143 * http://www.sleepycat.com/docs/ref/program/errorret.html#DB_INCOMPLETE
144 * http://www.sleepycat.com/docs/api_c/db_close.html
145 */
146 if (db_err == DB_INCOMPLETE)
147 db_err = 0;
148 #endif /* SVN_BDB_HAS_DB_INCOMPLETE */
149
150 SVN_ERR(BDB_WRAP(fs, msg, db_err));
151 }
152
153 return SVN_NO_ERROR;
154 }
155
156 /* Close whatever Berkeley DB resources are allocated to FS. */
157 static svn_error_t *
cleanup_fs(svn_fs_t * fs)158 cleanup_fs(svn_fs_t *fs)
159 {
160 base_fs_data_t *bfd = fs->fsap_data;
161 bdb_env_baton_t *bdb = (bfd ? bfd->bdb : NULL);
162
163 if (!bdb)
164 return SVN_NO_ERROR;
165
166 /* Close the databases. */
167 SVN_ERR(cleanup_fs_db(fs, &bfd->nodes, "nodes"));
168 SVN_ERR(cleanup_fs_db(fs, &bfd->revisions, "revisions"));
169 SVN_ERR(cleanup_fs_db(fs, &bfd->transactions, "transactions"));
170 SVN_ERR(cleanup_fs_db(fs, &bfd->copies, "copies"));
171 SVN_ERR(cleanup_fs_db(fs, &bfd->changes, "changes"));
172 SVN_ERR(cleanup_fs_db(fs, &bfd->representations, "representations"));
173 SVN_ERR(cleanup_fs_db(fs, &bfd->strings, "strings"));
174 SVN_ERR(cleanup_fs_db(fs, &bfd->uuids, "uuids"));
175 SVN_ERR(cleanup_fs_db(fs, &bfd->locks, "locks"));
176 SVN_ERR(cleanup_fs_db(fs, &bfd->lock_tokens, "lock-tokens"));
177 SVN_ERR(cleanup_fs_db(fs, &bfd->node_origins, "node-origins"));
178 SVN_ERR(cleanup_fs_db(fs, &bfd->checksum_reps, "checksum-reps"));
179 SVN_ERR(cleanup_fs_db(fs, &bfd->miscellaneous, "miscellaneous"));
180
181 /* Finally, close the environment. */
182 bfd->bdb = 0;
183 {
184 svn_error_t *err = svn_fs_bdb__close(bdb);
185 if (err)
186 return svn_error_createf
187 (err->apr_err, err,
188 _("Berkeley DB error for filesystem '%s'"
189 " while closing environment:\n"),
190 fs->path);
191 }
192 return SVN_NO_ERROR;
193 }
194
195 #if 0 /* Set to 1 for instrumenting. */
196 static void print_fs_stats(svn_fs_t *fs)
197 {
198 base_fs_data_t *bfd = fs->fsap_data;
199 DB_TXN_STAT *t;
200 DB_LOCK_STAT *l;
201 int db_err;
202
203 /* Print transaction statistics for this DB env. */
204 if ((db_err = bfd->bdb->env->txn_stat(bfd->bdb->env, &t, 0)) != 0)
205 fprintf(stderr, "Error running bfd->bdb->env->txn_stat(): %s",
206 db_strerror(db_err));
207 else
208 {
209 printf("*** DB transaction stats, right before closing env:\n");
210 printf(" Number of transactions currently active: %d\n",
211 t->st_nactive);
212 printf(" Max number of active transactions at any one time: %d\n",
213 t->st_maxnactive);
214 printf(" Number of transactions that have begun: %d\n",
215 t->st_nbegins);
216 printf(" Number of transactions that have aborted: %d\n",
217 t->st_naborts);
218 printf(" Number of transactions that have committed: %d\n",
219 t->st_ncommits);
220 printf(" Number of times a thread was forced to wait: %d\n",
221 t->st_region_wait);
222 printf(" Number of times a thread didn't need to wait: %d\n",
223 t->st_region_nowait);
224 printf("*** End DB transaction stats.\n\n");
225 }
226
227 /* Print transaction statistics for this DB env. */
228 if ((db_err = bfd->bdb->env->lock_stat(bfd->bdb->env, &l, 0)) != 0)
229 fprintf(stderr, "Error running bfd->bdb->env->lock_stat(): %s",
230 db_strerror(db_err));
231 else
232 {
233 printf("*** DB lock stats, right before closing env:\n");
234 printf(" The number of current locks: %d\n",
235 l->st_nlocks);
236 printf(" Max number of locks at any one time: %d\n",
237 l->st_maxnlocks);
238 printf(" Number of current lockers: %d\n",
239 l->st_nlockers);
240 printf(" Max number of lockers at any one time: %d\n",
241 l->st_maxnlockers);
242 printf(" Number of current objects: %d\n",
243 l->st_nobjects);
244 printf(" Max number of objects at any one time: %d\n",
245 l->st_maxnobjects);
246 printf(" Total number of locks requested: %d\n",
247 l->st_nrequests);
248 printf(" Total number of locks released: %d\n",
249 l->st_nreleases);
250 printf(" Total number of lock reqs failed because "
251 "DB_LOCK_NOWAIT was set: %d\n", l->st_nnowaits);
252 printf(" Total number of locks not immediately available "
253 "due to conflicts: %d\n", l->st_nconflicts);
254 printf(" Number of deadlocks detected: %d\n", l->st_ndeadlocks);
255 printf(" Number of times a thread waited before "
256 "obtaining the region lock: %d\n", l->st_region_wait);
257 printf(" Number of times a thread didn't have to wait: %d\n",
258 l->st_region_nowait);
259 printf("*** End DB lock stats.\n\n");
260 }
261
262 }
263 #else
264 # define print_fs_stats(fs)
265 #endif /* 0/1 */
266
267 /* An APR pool cleanup function for a filesystem. DATA must be a
268 pointer to the filesystem to clean up.
269
270 When the filesystem object's pool is freed, we want the resources
271 held by Berkeley DB to go away, just like everything else. So we
272 register this cleanup function with the filesystem's pool, and let
273 it take care of closing the databases, the environment, and any
274 other DB objects we might be using. APR calls this function before
275 actually freeing the pool's memory.
276
277 It's a pity that we can't return an svn_error_t object from an APR
278 cleanup function. For now, we return the rather generic
279 SVN_ERR_FS_CLEANUP, and pass the real svn_error_t to the registered
280 warning callback. */
281
282 static apr_status_t
cleanup_fs_apr(void * data)283 cleanup_fs_apr(void *data)
284 {
285 svn_fs_t *fs = data;
286 svn_error_t *err;
287
288 print_fs_stats(fs);
289
290 err = cleanup_fs(fs);
291 if (! err)
292 return APR_SUCCESS;
293
294 /* Darn. An error during cleanup. Call the warning handler to
295 try and do something "right" with this error. Note that
296 the default will simply abort(). */
297 (*fs->warning)(fs->warning_baton, err);
298
299 svn_error_clear(err);
300
301 return SVN_ERR_FS_CLEANUP;
302 }
303
304
305 static svn_error_t *
base_bdb_set_errcall(svn_fs_t * fs,void (* db_errcall_fcn)(const char * errpfx,char * msg))306 base_bdb_set_errcall(svn_fs_t *fs,
307 void (*db_errcall_fcn)(const char *errpfx, char *msg))
308 {
309 base_fs_data_t *bfd = fs->fsap_data;
310
311 SVN_ERR(svn_fs__check_fs(fs, TRUE));
312 bfd->bdb->error_info->user_callback = db_errcall_fcn;
313
314 return SVN_NO_ERROR;
315 }
316
317
318 /* Write the DB_CONFIG file. */
319 static svn_error_t *
bdb_write_config(svn_fs_t * fs)320 bdb_write_config(svn_fs_t *fs)
321 {
322 const char *dbconfig_file_name =
323 svn_dirent_join(fs->path, BDB_CONFIG_FILE, fs->pool);
324 apr_file_t *dbconfig_file = NULL;
325 int i;
326
327 static const char dbconfig_contents[] =
328 "# This is the configuration file for the Berkeley DB environment\n"
329 "# used by your Subversion repository.\n"
330 "# You must run 'svnadmin recover' whenever you modify this file,\n"
331 "# for your changes to take effect.\n"
332 "\n"
333 "### Lock subsystem\n"
334 "#\n"
335 "# Make sure you read the documentation at:\n"
336 "#\n"
337 "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/lock_max.html\n"
338 "#\n"
339 "# before tweaking these values.\n"
340 "#\n"
341 "set_lk_max_locks 2000\n"
342 "set_lk_max_lockers 2000\n"
343 "set_lk_max_objects 2000\n"
344 "\n"
345 "### Log file subsystem\n"
346 "#\n"
347 "# Make sure you read the documentation at:\n"
348 "#\n"
349 "# http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_bsize.html\n"
350 "# http://docs.oracle.com/cd/E17076_02/html/api_reference/C/envset_lg_max.html\n"
351 "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_limits.html\n"
352 "#\n"
353 "# Increase the size of the in-memory log buffer from the default\n"
354 "# of 32 Kbytes to 256 Kbytes. Decrease the log file size from\n"
355 "# 10 Mbytes to 1 Mbyte. This will help reduce the amount of disk\n"
356 "# space required for hot backups. The size of the log file must be\n"
357 "# at least four times the size of the in-memory log buffer.\n"
358 "#\n"
359 "# Note: Decreasing the in-memory buffer size below 256 Kbytes will hurt\n"
360 "# hurt commit performance. For details, see:\n"
361 "#\n"
362 "# http://svn.haxx.se/dev/archive-2002-02/0141.shtml\n"
363 "#\n"
364 "set_lg_bsize 262144\n"
365 "set_lg_max 1048576\n"
366 "#\n"
367 "# If you see \"log region out of memory\" errors, bump lg_regionmax.\n"
368 "# For more information, see:\n"
369 "#\n"
370 "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
371 "# http://svn.haxx.se/users/archive-2004-10/1000.shtml\n"
372 "#\n"
373 "set_lg_regionmax 131072\n"
374 "#\n"
375 /* ### Configure this with "svnadmin create --bdb-cache-size" */
376 "# The default cache size in BDB is only 256k. As explained in\n"
377 "# http://svn.haxx.se/dev/archive-2004-12/0368.shtml, this is too\n"
378 "# small for most applications. Bump this number if \"db_stat -m\"\n"
379 "# shows too many cache misses.\n"
380 "#\n"
381 "set_cachesize 0 1048576 1\n";
382
383 /* Run-time configurable options.
384 Each option set consists of a minimum required BDB version, a
385 config hash key, a header, an inactive form and an active
386 form. We always write the header; then, depending on the
387 run-time configuration and the BDB version we're compiling
388 against, we write either the active or inactive form of the
389 value. */
390 static const struct
391 {
392 int bdb_major;
393 int bdb_minor;
394 const char *config_key;
395 const char *header;
396 const char *inactive;
397 const char *active;
398 } dbconfig_options[] = {
399 /* Controlled by "svnadmin create --bdb-txn-nosync" */
400 { 4, 0, SVN_FS_CONFIG_BDB_TXN_NOSYNC,
401 /* header */
402 "#\n"
403 "# Disable fsync of log files on transaction commit. Read the\n"
404 "# documentation about DB_TXN_NOSYNC at:\n"
405 "#\n"
406 "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
407 "#\n"
408 "# [requires Berkeley DB 4.0]\n"
409 "#\n",
410 /* inactive */
411 "#set_flags DB_TXN_NOSYNC\n",
412 /* active */
413 "set_flags DB_TXN_NOSYNC\n" },
414 /* Controlled by "svnadmin create --bdb-log-keep" */
415 { 4, 2, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE,
416 /* header */
417 "#\n"
418 "# Enable automatic removal of unused transaction log files.\n"
419 "# Read the documentation about DB_LOG_AUTOREMOVE at:\n"
420 "#\n"
421 "# http://docs.oracle.com/cd/E17076_02/html/programmer_reference/log_config.html\n"
422 "#\n"
423 "# [requires Berkeley DB 4.2]\n"
424 "#\n",
425 /* inactive */
426 "#set_flags DB_LOG_AUTOREMOVE\n",
427 /* active */
428 "set_flags DB_LOG_AUTOREMOVE\n" },
429 };
430 static const int dbconfig_options_length =
431 sizeof(dbconfig_options)/sizeof(*dbconfig_options);
432
433
434 SVN_ERR(svn_io_file_open(&dbconfig_file, dbconfig_file_name,
435 APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
436 fs->pool));
437
438 SVN_ERR(svn_io_file_write_full(dbconfig_file, dbconfig_contents,
439 sizeof(dbconfig_contents) - 1, NULL,
440 fs->pool));
441
442 /* Write the variable DB_CONFIG flags. */
443 for (i = 0; i < dbconfig_options_length; ++i)
444 {
445 void *value = NULL;
446 const char *choice;
447
448 if (fs->config)
449 {
450 value = svn_hash_gets(fs->config, dbconfig_options[i].config_key);
451 }
452
453 SVN_ERR(svn_io_file_write_full(dbconfig_file,
454 dbconfig_options[i].header,
455 strlen(dbconfig_options[i].header),
456 NULL, fs->pool));
457
458 if (((DB_VERSION_MAJOR == dbconfig_options[i].bdb_major
459 && DB_VERSION_MINOR >= dbconfig_options[i].bdb_minor)
460 || DB_VERSION_MAJOR > dbconfig_options[i].bdb_major)
461 && value != NULL && strcmp(value, "0") != 0)
462 choice = dbconfig_options[i].active;
463 else
464 choice = dbconfig_options[i].inactive;
465
466 SVN_ERR(svn_io_file_write_full(dbconfig_file, choice, strlen(choice),
467 NULL, fs->pool));
468 }
469
470 return svn_io_file_close(dbconfig_file, fs->pool);
471 }
472
473 static svn_error_t *
base_bdb_refresh_revision(svn_fs_t * fs,apr_pool_t * scratch_pool)474 base_bdb_refresh_revision(svn_fs_t *fs,
475 apr_pool_t *scratch_pool)
476 {
477 return SVN_NO_ERROR;
478 }
479
480 static svn_error_t *
base_bdb_info_format(int * fs_format,svn_version_t ** supports_version,svn_fs_t * fs,apr_pool_t * result_pool,apr_pool_t * scratch_pool)481 base_bdb_info_format(int *fs_format,
482 svn_version_t **supports_version,
483 svn_fs_t *fs,
484 apr_pool_t *result_pool,
485 apr_pool_t *scratch_pool)
486 {
487 base_fs_data_t *bfd = fs->fsap_data;
488
489 *fs_format = bfd->format;
490 *supports_version = apr_palloc(result_pool, sizeof(svn_version_t));
491
492 (*supports_version)->major = SVN_VER_MAJOR;
493 (*supports_version)->minor = 0;
494 (*supports_version)->patch = 0;
495 (*supports_version)->tag = "";
496
497 switch (bfd->format)
498 {
499 case 1:
500 break;
501 case 2:
502 (*supports_version)->minor = 4;
503 break;
504 case 3:
505 (*supports_version)->minor = 5;
506 break;
507 case 4:
508 (*supports_version)->minor = 6;
509 break;
510 #ifdef SVN_DEBUG
511 # if SVN_FS_BASE__FORMAT_NUMBER != 4
512 # error "Need to add a 'case' statement here"
513 # endif
514 #endif
515 }
516
517 return SVN_NO_ERROR;
518 }
519
520 static svn_error_t *
base_bdb_info_config_files(apr_array_header_t ** files,svn_fs_t * fs,apr_pool_t * result_pool,apr_pool_t * scratch_pool)521 base_bdb_info_config_files(apr_array_header_t **files,
522 svn_fs_t *fs,
523 apr_pool_t *result_pool,
524 apr_pool_t *scratch_pool)
525 {
526 *files = apr_array_make(result_pool, 1, sizeof(const char *));
527 APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path,
528 BDB_CONFIG_FILE,
529 result_pool);
530 return SVN_NO_ERROR;
531 }
532
533 static svn_error_t *
base_bdb_verify_root(svn_fs_root_t * root,apr_pool_t * scratch_pool)534 base_bdb_verify_root(svn_fs_root_t *root,
535 apr_pool_t *scratch_pool)
536 {
537 /* Verifying is currently a no op for BDB. */
538 return SVN_NO_ERROR;
539 }
540
541 static svn_error_t *
base_bdb_freeze(svn_fs_t * fs,svn_fs_freeze_func_t freeze_func,void * freeze_baton,apr_pool_t * pool)542 base_bdb_freeze(svn_fs_t *fs,
543 svn_fs_freeze_func_t freeze_func,
544 void *freeze_baton,
545 apr_pool_t *pool)
546 {
547 SVN__NOT_IMPLEMENTED();
548 }
549
550
551 /* Creating a new filesystem */
552
553 static fs_vtable_t fs_vtable = {
554 svn_fs_base__youngest_rev,
555 base_bdb_refresh_revision,
556 svn_fs_base__revision_prop,
557 svn_fs_base__revision_proplist,
558 svn_fs_base__change_rev_prop,
559 svn_fs_base__set_uuid,
560 svn_fs_base__revision_root,
561 svn_fs_base__begin_txn,
562 svn_fs_base__open_txn,
563 svn_fs_base__purge_txn,
564 svn_fs_base__list_transactions,
565 svn_fs_base__deltify,
566 svn_fs_base__lock,
567 svn_fs_base__generate_lock_token,
568 svn_fs_base__unlock,
569 svn_fs_base__get_lock,
570 svn_fs_base__get_locks,
571 base_bdb_info_format,
572 base_bdb_info_config_files,
573 NULL /* info_fsap */,
574 base_bdb_verify_root,
575 base_bdb_freeze,
576 base_bdb_set_errcall,
577 };
578
579 /* Where the format number is stored. */
580 #define FORMAT_FILE "format"
581
582 /* Depending on CREATE, create or open the environment and databases
583 for filesystem FS in PATH. */
584 static svn_error_t *
open_databases(svn_fs_t * fs,svn_boolean_t create,int format,const char * path)585 open_databases(svn_fs_t *fs,
586 svn_boolean_t create,
587 int format,
588 const char *path)
589 {
590 base_fs_data_t *bfd;
591
592 SVN_ERR(svn_fs__check_fs(fs, FALSE));
593
594 bfd = apr_pcalloc(fs->pool, sizeof(*bfd));
595 fs->vtable = &fs_vtable;
596 fs->fsap_data = bfd;
597
598 /* Initialize the fs's path. */
599 fs->path = apr_pstrdup(fs->pool, path);
600
601 if (create)
602 SVN_ERR(bdb_write_config(fs));
603
604 /* Create the Berkeley DB environment. */
605 {
606 svn_error_t *err = svn_fs_bdb__open(&(bfd->bdb), path,
607 SVN_BDB_STANDARD_ENV_FLAGS,
608 0666, fs->pool);
609 if (err)
610 {
611 if (create)
612 return svn_error_createf
613 (err->apr_err, err,
614 _("Berkeley DB error for filesystem '%s'"
615 " while creating environment:\n"),
616 fs->path);
617 else
618 return svn_error_createf
619 (err->apr_err, err,
620 _("Berkeley DB error for filesystem '%s'"
621 " while opening environment:\n"),
622 fs->path);
623 }
624 }
625
626 /* We must register the FS cleanup function *after* opening the
627 environment, so that it's run before the environment baton
628 cleanup. */
629 apr_pool_cleanup_register(fs->pool, fs, cleanup_fs_apr,
630 apr_pool_cleanup_null);
631
632
633 /* Create the databases in the environment. */
634 SVN_ERR(BDB_WRAP(fs, (create
635 ? N_("creating 'nodes' table")
636 : N_("opening 'nodes' table")),
637 svn_fs_bdb__open_nodes_table(&bfd->nodes,
638 bfd->bdb->env,
639 create)));
640 SVN_ERR(BDB_WRAP(fs, (create
641 ? N_("creating 'revisions' table")
642 : N_("opening 'revisions' table")),
643 svn_fs_bdb__open_revisions_table(&bfd->revisions,
644 bfd->bdb->env,
645 create)));
646 SVN_ERR(BDB_WRAP(fs, (create
647 ? N_("creating 'transactions' table")
648 : N_("opening 'transactions' table")),
649 svn_fs_bdb__open_transactions_table(&bfd->transactions,
650 bfd->bdb->env,
651 create)));
652 SVN_ERR(BDB_WRAP(fs, (create
653 ? N_("creating 'copies' table")
654 : N_("opening 'copies' table")),
655 svn_fs_bdb__open_copies_table(&bfd->copies,
656 bfd->bdb->env,
657 create)));
658 SVN_ERR(BDB_WRAP(fs, (create
659 ? N_("creating 'changes' table")
660 : N_("opening 'changes' table")),
661 svn_fs_bdb__open_changes_table(&bfd->changes,
662 bfd->bdb->env,
663 create)));
664 SVN_ERR(BDB_WRAP(fs, (create
665 ? N_("creating 'representations' table")
666 : N_("opening 'representations' table")),
667 svn_fs_bdb__open_reps_table(&bfd->representations,
668 bfd->bdb->env,
669 create)));
670 SVN_ERR(BDB_WRAP(fs, (create
671 ? N_("creating 'strings' table")
672 : N_("opening 'strings' table")),
673 svn_fs_bdb__open_strings_table(&bfd->strings,
674 bfd->bdb->env,
675 create)));
676 SVN_ERR(BDB_WRAP(fs, (create
677 ? N_("creating 'uuids' table")
678 : N_("opening 'uuids' table")),
679 svn_fs_bdb__open_uuids_table(&bfd->uuids,
680 bfd->bdb->env,
681 create)));
682 SVN_ERR(BDB_WRAP(fs, (create
683 ? N_("creating 'locks' table")
684 : N_("opening 'locks' table")),
685 svn_fs_bdb__open_locks_table(&bfd->locks,
686 bfd->bdb->env,
687 create)));
688 SVN_ERR(BDB_WRAP(fs, (create
689 ? N_("creating 'lock-tokens' table")
690 : N_("opening 'lock-tokens' table")),
691 svn_fs_bdb__open_lock_tokens_table(&bfd->lock_tokens,
692 bfd->bdb->env,
693 create)));
694
695 if (format >= SVN_FS_BASE__MIN_NODE_ORIGINS_FORMAT)
696 {
697 SVN_ERR(BDB_WRAP(fs, (create
698 ? N_("creating 'node-origins' table")
699 : N_("opening 'node-origins' table")),
700 svn_fs_bdb__open_node_origins_table(&bfd->node_origins,
701 bfd->bdb->env,
702 create)));
703 }
704
705 if (format >= SVN_FS_BASE__MIN_MISCELLANY_FORMAT)
706 {
707 SVN_ERR(BDB_WRAP(fs, (create
708 ? N_("creating 'miscellaneous' table")
709 : N_("opening 'miscellaneous' table")),
710 svn_fs_bdb__open_miscellaneous_table(&bfd->miscellaneous,
711 bfd->bdb->env,
712 create)));
713 }
714
715 if (format >= SVN_FS_BASE__MIN_REP_SHARING_FORMAT)
716 {
717 SVN_ERR(BDB_WRAP(fs, (create
718 ? N_("creating 'checksum-reps' table")
719 : N_("opening 'checksum-reps' table")),
720 svn_fs_bdb__open_checksum_reps_table(&bfd->checksum_reps,
721 bfd->bdb->env,
722 create)));
723 }
724
725 return SVN_NO_ERROR;
726 }
727
728
729 /* Called by functions that initialize an svn_fs_t struct, after that
730 initialization is done, to populate svn_fs_t->uuid. */
731 static svn_error_t *
populate_opened_fs(svn_fs_t * fs,apr_pool_t * scratch_pool)732 populate_opened_fs(svn_fs_t *fs, apr_pool_t *scratch_pool)
733 {
734 SVN_ERR(svn_fs_base__populate_uuid(fs, scratch_pool));
735 return SVN_NO_ERROR;
736 }
737
738 static svn_error_t *
base_create(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)739 base_create(svn_fs_t *fs,
740 const char *path,
741 svn_mutex__t *common_pool_lock,
742 apr_pool_t *scratch_pool,
743 apr_pool_t *common_pool)
744 {
745 int format = SVN_FS_BASE__FORMAT_NUMBER;
746 svn_error_t *svn_err;
747
748 /* See if compatibility with older versions was explicitly requested. */
749 if (fs->config)
750 {
751 svn_version_t *compatible_version;
752 SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config,
753 scratch_pool));
754
755 /* select format number */
756 switch(compatible_version->minor)
757 {
758 case 0:
759 case 1:
760 case 2:
761 case 3: format = 1;
762 break;
763
764 case 4: format = 2;
765 break;
766
767 case 5: format = 3;
768 break;
769
770 default:format = SVN_FS_BASE__FORMAT_NUMBER;
771 }
772 }
773
774 /* Create the environment and databases. */
775 svn_err = open_databases(fs, TRUE, format, path);
776 if (svn_err) goto error;
777
778 /* Initialize the DAG subsystem. */
779 svn_err = svn_fs_base__dag_init_fs(fs);
780 if (svn_err) goto error;
781
782 /* This filesystem is ready. Stamp it with a format number. */
783 svn_err = svn_io_write_version_file(svn_dirent_join(fs->path, FORMAT_FILE,
784 scratch_pool),
785 format, scratch_pool);
786 if (svn_err) goto error;
787
788 ((base_fs_data_t *) fs->fsap_data)->format = format;
789
790 SVN_ERR(populate_opened_fs(fs, scratch_pool));
791 return SVN_NO_ERROR;
792
793 error:
794 return svn_error_compose_create(svn_err,
795 svn_error_trace(cleanup_fs(fs)));
796 }
797
798
799 /* Gaining access to an existing Berkeley DB-based filesystem. */
800
801 svn_error_t *
svn_fs_base__test_required_feature_format(svn_fs_t * fs,const char * feature,int requires)802 svn_fs_base__test_required_feature_format(svn_fs_t *fs,
803 const char *feature,
804 int requires)
805 {
806 base_fs_data_t *bfd = fs->fsap_data;
807 if (bfd->format < requires)
808 return svn_error_createf
809 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
810 _("The '%s' feature requires version %d of the filesystem schema; "
811 "filesystem '%s' uses only version %d"),
812 feature, requires, fs->path, bfd->format);
813 return SVN_NO_ERROR;
814 }
815
816 /* Return the error SVN_ERR_FS_UNSUPPORTED_FORMAT if FS's format
817 number is not the same as the format number supported by this
818 Subversion. */
819 static svn_error_t *
check_format(int format)820 check_format(int format)
821 {
822 /* We currently support any format less than the compiled format number
823 simultaneously. */
824 if (format <= SVN_FS_BASE__FORMAT_NUMBER)
825 return SVN_NO_ERROR;
826
827 return svn_error_createf(
828 SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL,
829 _("Expected FS format '%d'; found format '%d'"),
830 SVN_FS_BASE__FORMAT_NUMBER, format);
831 }
832
833 static svn_error_t *
base_open(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * scratch_pool,apr_pool_t * common_pool)834 base_open(svn_fs_t *fs,
835 const char *path,
836 svn_mutex__t *common_pool_lock,
837 apr_pool_t *scratch_pool,
838 apr_pool_t *common_pool)
839 {
840 int format;
841 svn_error_t *svn_err;
842 svn_boolean_t write_format_file = FALSE;
843
844 /* Read the FS format number. */
845 svn_err = svn_io_read_version_file(&format,
846 svn_dirent_join(path, FORMAT_FILE,
847 scratch_pool),
848 scratch_pool);
849 if (svn_err && APR_STATUS_IS_ENOENT(svn_err->apr_err))
850 {
851 /* Pre-1.2 filesystems did not have a format file (you could say
852 they were format "0"), so they get upgraded on the fly.
853 However, we stopped "upgrading on the fly" in 1.5, so older
854 filesystems should only be bumped to 1.3, which is format "1". */
855 svn_error_clear(svn_err);
856 svn_err = SVN_NO_ERROR;
857 format = 1;
858 write_format_file = TRUE;
859 }
860 else if (svn_err)
861 goto error;
862
863 /* Create the environment and databases. */
864 svn_err = open_databases(fs, FALSE, format, path);
865 if (svn_err) goto error;
866
867 ((base_fs_data_t *) fs->fsap_data)->format = format;
868 SVN_ERR(check_format(format));
869
870 /* If we lack a format file, write one. */
871 if (write_format_file)
872 {
873 svn_err = svn_io_write_version_file(svn_dirent_join(path, FORMAT_FILE,
874 scratch_pool),
875 format, scratch_pool);
876 if (svn_err) goto error;
877 }
878
879 SVN_ERR(populate_opened_fs(fs, scratch_pool));
880 return SVN_NO_ERROR;
881
882 error:
883 return svn_error_compose_create(svn_err,
884 svn_error_trace(cleanup_fs(fs)));
885 }
886
887
888 /* Running recovery on a Berkeley DB-based filesystem. */
889
890
891 /* Recover a database at PATH. Perform catastrophic recovery if FATAL
892 is TRUE. Use POOL for temporary allocation. */
893 static svn_error_t *
bdb_recover(const char * path,svn_boolean_t fatal,apr_pool_t * pool)894 bdb_recover(const char *path, svn_boolean_t fatal, apr_pool_t *pool)
895 {
896 bdb_env_baton_t *bdb;
897
898 /* Here's the comment copied from db_recover.c:
899
900 Initialize the environment -- we don't actually do anything
901 else, that all that's needed to run recovery.
902
903 Note that we specify a private environment, as we're about to
904 create a region, and we don't want to leave it around. If we
905 leave the region around, the application that should create it
906 will simply join it instead, and will then be running with
907 incorrectly sized (and probably terribly small) caches. */
908
909 /* Note that since we're using a private environment, we shoudl
910 /not/ initialize locking. We want the environment files to go
911 away. */
912
913 SVN_ERR(svn_fs_bdb__open(&bdb, path,
914 ((fatal ? DB_RECOVER_FATAL : DB_RECOVER)
915 | SVN_BDB_PRIVATE_ENV_FLAGS),
916 0666, pool));
917 return svn_fs_bdb__close(bdb);
918 }
919
920 static svn_error_t *
base_open_for_recovery(svn_fs_t * fs,const char * path,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)921 base_open_for_recovery(svn_fs_t *fs,
922 const char *path,
923 svn_mutex__t *common_pool_lock,
924 apr_pool_t *pool,
925 apr_pool_t *common_pool)
926 {
927 /* Just stash the path in the fs pointer - it's all we really need. */
928 fs->path = apr_pstrdup(fs->pool, path);
929
930 return SVN_NO_ERROR;
931 }
932
933 static svn_error_t *
base_upgrade(svn_fs_t * fs,const char * path,svn_fs_upgrade_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)934 base_upgrade(svn_fs_t *fs,
935 const char *path,
936 svn_fs_upgrade_notify_t notify_func,
937 void *notify_baton,
938 svn_cancel_func_t cancel_func,
939 void *cancel_baton,
940 svn_mutex__t *common_pool_lock,
941 apr_pool_t *pool,
942 apr_pool_t *common_pool)
943 {
944 const char *version_file_path;
945 int old_format_number;
946 svn_error_t *err;
947
948 version_file_path = svn_dirent_join(path, FORMAT_FILE, pool);
949
950 /* Read the old number so we've got it on hand later on. */
951 err = svn_io_read_version_file(&old_format_number, version_file_path, pool);
952 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
953 {
954 /* Pre-1.2 filesystems do not have a 'format' file. */
955 old_format_number = 0;
956 svn_error_clear(err);
957 err = SVN_NO_ERROR;
958 }
959 SVN_ERR(err);
960 SVN_ERR(check_format(old_format_number));
961
962 /* Bump the format file's stored version number. */
963 SVN_ERR(svn_io_write_version_file(version_file_path,
964 SVN_FS_BASE__FORMAT_NUMBER, pool));
965 if (notify_func)
966 SVN_ERR(notify_func(notify_baton, SVN_FS_BASE__FORMAT_NUMBER,
967 svn_fs_upgrade_format_bumped, pool));
968
969 /* Check and see if we need to record the "bump" revision. */
970 if (old_format_number < SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT)
971 {
972 apr_pool_t *subpool = svn_pool_create(pool);
973 svn_revnum_t youngest_rev;
974 const char *value;
975
976 /* Open the filesystem in a subpool (so we can control its
977 closure) and do our fiddling.
978
979 NOTE: By using base_open() here instead of open_databases(),
980 we will end up re-reading the format file that we just wrote.
981 But it's better to use the existing encapsulation of "opening
982 the filesystem" rather than duplicating (or worse, partially
983 duplicating) that logic here. */
984 SVN_ERR(base_open(fs, path, common_pool_lock, subpool, common_pool));
985
986 /* Fetch the youngest rev, and record it */
987 SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, subpool));
988 value = apr_psprintf(subpool, "%ld", youngest_rev);
989 SVN_ERR(svn_fs_base__miscellaneous_set
990 (fs, SVN_FS_BASE__MISC_FORWARD_DELTA_UPGRADE,
991 value, subpool));
992 svn_pool_destroy(subpool);
993 }
994
995 return SVN_NO_ERROR;
996 }
997
998 static svn_error_t *
base_verify(svn_fs_t * fs,const char * path,svn_revnum_t start,svn_revnum_t end,svn_fs_progress_notify_func_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)999 base_verify(svn_fs_t *fs, const char *path,
1000 svn_revnum_t start,
1001 svn_revnum_t end,
1002 svn_fs_progress_notify_func_t notify_func,
1003 void *notify_baton,
1004 svn_cancel_func_t cancel_func,
1005 void *cancel_baton,
1006 svn_mutex__t *common_pool_lock,
1007 apr_pool_t *pool,
1008 apr_pool_t *common_pool)
1009 {
1010 /* Verifying is currently a no op for BDB. */
1011 return SVN_NO_ERROR;
1012 }
1013
1014 static svn_error_t *
base_bdb_recover(svn_fs_t * fs,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)1015 base_bdb_recover(svn_fs_t *fs,
1016 svn_cancel_func_t cancel_func, void *cancel_baton,
1017 apr_pool_t *pool)
1018 {
1019 /* The fs pointer is a fake created in base_open_for_recovery above.
1020 We only care about the path. */
1021 return bdb_recover(fs->path, FALSE, pool);
1022 }
1023
1024 static svn_error_t *
base_bdb_pack(svn_fs_t * fs,const char * path,svn_fs_pack_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)1025 base_bdb_pack(svn_fs_t *fs,
1026 const char *path,
1027 svn_fs_pack_notify_t notify_func,
1028 void *notify_baton,
1029 svn_cancel_func_t cancel,
1030 void *cancel_baton,
1031 svn_mutex__t *common_pool_lock,
1032 apr_pool_t *pool,
1033 apr_pool_t *common_pool)
1034 {
1035 /* Packing is currently a no op for BDB. */
1036 return SVN_NO_ERROR;
1037 }
1038
1039
1040
1041 /* Running the 'archive' command on a Berkeley DB-based filesystem. */
1042
1043
1044 static svn_error_t *
base_bdb_logfiles(apr_array_header_t ** logfiles,const char * path,svn_boolean_t only_unused,apr_pool_t * pool)1045 base_bdb_logfiles(apr_array_header_t **logfiles,
1046 const char *path,
1047 svn_boolean_t only_unused,
1048 apr_pool_t *pool)
1049 {
1050 bdb_env_baton_t *bdb;
1051 char **filelist;
1052 char **filename;
1053 u_int32_t flags = only_unused ? 0 : DB_ARCH_LOG;
1054
1055 *logfiles = apr_array_make(pool, 4, sizeof(const char *));
1056
1057 SVN_ERR(svn_fs_bdb__open(&bdb, path,
1058 SVN_BDB_STANDARD_ENV_FLAGS,
1059 0666, pool));
1060 SVN_BDB_ERR(bdb, bdb->env->log_archive(bdb->env, &filelist, flags));
1061
1062 if (filelist == NULL)
1063 return svn_fs_bdb__close(bdb);
1064
1065 for (filename = filelist; *filename != NULL; ++filename)
1066 {
1067 APR_ARRAY_PUSH(*logfiles, const char *) = apr_pstrdup(pool, *filename);
1068 }
1069
1070 free(filelist);
1071
1072 return svn_fs_bdb__close(bdb);
1073 }
1074
1075
1076
1077 /* Copying a live Berkeley DB-base filesystem. */
1078
1079 /**
1080 * Delete all unused log files from DBD enviroment at @a live_path that exist
1081 * in @a backup_path.
1082 */
1083 static svn_error_t *
svn_fs_base__clean_logs(const char * live_path,const char * backup_path,apr_pool_t * pool)1084 svn_fs_base__clean_logs(const char *live_path,
1085 const char *backup_path,
1086 apr_pool_t *pool)
1087 {
1088 apr_array_header_t *logfiles;
1089
1090 SVN_ERR(base_bdb_logfiles(&logfiles,
1091 live_path,
1092 TRUE, /* Only unused logs */
1093 pool));
1094
1095 { /* Process unused logs from live area */
1096 int idx;
1097 apr_pool_t *subpool = svn_pool_create(pool);
1098
1099 /* Process log files. */
1100 for (idx = 0; idx < logfiles->nelts; idx++)
1101 {
1102 const char *log_file = APR_ARRAY_IDX(logfiles, idx, const char *);
1103 const char *live_log_path;
1104 const char *backup_log_path;
1105
1106 svn_pool_clear(subpool);
1107 live_log_path = svn_dirent_join(live_path, log_file, subpool);
1108 backup_log_path = svn_dirent_join(backup_path, log_file, subpool);
1109
1110 { /* Compare files. No point in using MD5 and wasting CPU cycles as we
1111 got full copies of both logs */
1112
1113 svn_boolean_t files_match = FALSE;
1114 svn_node_kind_t kind;
1115
1116 /* Check to see if there is a corresponding log file in the backup
1117 directory */
1118 SVN_ERR(svn_io_check_path(backup_log_path, &kind, pool));
1119
1120 /* If the copy of the log exists, compare them */
1121 if (kind == svn_node_file)
1122 SVN_ERR(svn_io_files_contents_same_p(&files_match,
1123 live_log_path,
1124 backup_log_path,
1125 subpool));
1126
1127 /* If log files do not match, go to the next log file. */
1128 if (!files_match)
1129 continue;
1130 }
1131
1132 SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, subpool));
1133 }
1134
1135 svn_pool_destroy(subpool);
1136 }
1137
1138 return SVN_NO_ERROR;
1139 }
1140
1141
1142 /* DB_ENV->get_flags() and DB->get_pagesize() don't exist prior to
1143 Berkeley DB 4.2. */
1144 #if SVN_BDB_VERSION_AT_LEAST(4, 2)
1145
1146 /* Open the BDB environment at PATH and compare its configuration
1147 flags with FLAGS. If every flag in FLAGS is set in the
1148 environment, then set *MATCH to true. Else set *MATCH to false. */
1149 static svn_error_t *
check_env_flags(svn_boolean_t * match,u_int32_t flags,const char * path,apr_pool_t * pool)1150 check_env_flags(svn_boolean_t *match,
1151 u_int32_t flags,
1152 const char *path,
1153 apr_pool_t *pool)
1154 {
1155 bdb_env_baton_t *bdb;
1156 #if SVN_BDB_VERSION_AT_LEAST(4, 7)
1157 int flag_state;
1158 #else
1159 u_int32_t envflags;
1160 #endif
1161
1162 SVN_ERR(svn_fs_bdb__open(&bdb, path,
1163 SVN_BDB_STANDARD_ENV_FLAGS,
1164 0666, pool));
1165 #if SVN_BDB_VERSION_AT_LEAST(4, 7)
1166 SVN_BDB_ERR(bdb, bdb->env->log_get_config(bdb->env, flags, &flag_state));
1167 #else
1168 SVN_BDB_ERR(bdb, bdb->env->get_flags(bdb->env, &envflags));
1169 #endif
1170
1171 SVN_ERR(svn_fs_bdb__close(bdb));
1172
1173 #if SVN_BDB_VERSION_AT_LEAST(4, 7)
1174 if (flag_state == 0)
1175 #else
1176 if (flags & envflags)
1177 #endif
1178 *match = TRUE;
1179 else
1180 *match = FALSE;
1181
1182 return SVN_NO_ERROR;
1183 }
1184
1185
1186 /* Set *PAGESIZE to the size of pages used to hold items in the
1187 database environment located at PATH.
1188 */
1189 static svn_error_t *
get_db_pagesize(u_int32_t * pagesize,const char * path,apr_pool_t * pool)1190 get_db_pagesize(u_int32_t *pagesize,
1191 const char *path,
1192 apr_pool_t *pool)
1193 {
1194 bdb_env_baton_t *bdb;
1195 DB *nodes_table;
1196
1197 SVN_ERR(svn_fs_bdb__open(&bdb, path,
1198 SVN_BDB_STANDARD_ENV_FLAGS,
1199 0666, pool));
1200
1201 /* ### We're only asking for the pagesize on the 'nodes' table.
1202 Is this enough? We never call DB->set_pagesize() on any of
1203 our tables, so presumably BDB is using the same default
1204 pagesize for all our databases, right? */
1205 SVN_BDB_ERR(bdb, svn_fs_bdb__open_nodes_table(&nodes_table, bdb->env,
1206 FALSE));
1207 SVN_BDB_ERR(bdb, nodes_table->get_pagesize(nodes_table, pagesize));
1208 SVN_BDB_ERR(bdb, nodes_table->close(nodes_table, 0));
1209
1210 return svn_fs_bdb__close(bdb);
1211 }
1212 #endif /* SVN_BDB_VERSION_AT_LEAST(4, 2) */
1213
1214
1215 /* Copy FILENAME from SRC_DIR to DST_DIR in byte increments of size
1216 CHUNKSIZE. The read/write buffer of size CHUNKSIZE will be
1217 allocated in POOL. If ALLOW_MISSING is set, we won't make a fuss
1218 if FILENAME isn't found in SRC_DIR; otherwise, we will. */
1219 static svn_error_t *
copy_db_file_safely(const char * src_dir,const char * dst_dir,const char * filename,u_int32_t chunksize,svn_boolean_t allow_missing,apr_pool_t * pool)1220 copy_db_file_safely(const char *src_dir,
1221 const char *dst_dir,
1222 const char *filename,
1223 u_int32_t chunksize,
1224 svn_boolean_t allow_missing,
1225 apr_pool_t *pool)
1226 {
1227 apr_file_t *s = NULL, *d = NULL; /* init to null important for APR */
1228 const char *file_src_path = svn_dirent_join(src_dir, filename, pool);
1229 const char *file_dst_path = svn_dirent_join(dst_dir, filename, pool);
1230 svn_error_t *err;
1231 char *buf;
1232
1233 /* Open source file. If it's missing and that's allowed, there's
1234 nothing more to do here. */
1235 err = svn_io_file_open(&s, file_src_path,
1236 (APR_READ | APR_LARGEFILE),
1237 APR_OS_DEFAULT, pool);
1238 if (err && APR_STATUS_IS_ENOENT(err->apr_err) && allow_missing)
1239 {
1240 svn_error_clear(err);
1241 return SVN_NO_ERROR;
1242 }
1243 SVN_ERR(err);
1244
1245 /* Open destination file. */
1246 SVN_ERR(svn_io_file_open(&d, file_dst_path, (APR_WRITE | APR_CREATE |
1247 APR_LARGEFILE),
1248 APR_OS_DEFAULT, pool));
1249
1250 /* Allocate our read/write buffer. */
1251 buf = apr_palloc(pool, chunksize);
1252
1253 /* Copy bytes till the cows come home. */
1254 while (1)
1255 {
1256 apr_size_t bytes_this_time = chunksize;
1257 svn_error_t *read_err, *write_err;
1258
1259 /* Read 'em. */
1260 if ((read_err = svn_io_file_read(s, buf, &bytes_this_time, pool)))
1261 {
1262 if (APR_STATUS_IS_EOF(read_err->apr_err))
1263 svn_error_clear(read_err);
1264 else
1265 {
1266 svn_error_clear(svn_io_file_close(s, pool));
1267 svn_error_clear(svn_io_file_close(d, pool));
1268 return read_err;
1269 }
1270 }
1271
1272 /* Write 'em. */
1273 if ((write_err = svn_io_file_write_full(d, buf, bytes_this_time, NULL,
1274 pool)))
1275 {
1276 svn_error_clear(svn_io_file_close(s, pool));
1277 svn_error_clear(svn_io_file_close(d, pool));
1278 return write_err;
1279 }
1280
1281 /* read_err is either NULL, or a dangling pointer - but it is only a
1282 dangling pointer if it used to be an EOF error. */
1283 if (read_err)
1284 {
1285 SVN_ERR(svn_io_file_close(s, pool));
1286 SVN_ERR(svn_io_file_close(d, pool));
1287 break; /* got EOF on read, all files closed, all done. */
1288 }
1289 }
1290
1291 return SVN_NO_ERROR;
1292 }
1293
1294
1295
1296
1297 static svn_error_t *
base_hotcopy(svn_fs_t * src_fs,svn_fs_t * dst_fs,const char * src_path,const char * dest_path,svn_boolean_t clean_logs,svn_boolean_t incremental,svn_fs_hotcopy_notify_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_mutex__t * common_pool_lock,apr_pool_t * pool,apr_pool_t * common_pool)1298 base_hotcopy(svn_fs_t *src_fs,
1299 svn_fs_t *dst_fs,
1300 const char *src_path,
1301 const char *dest_path,
1302 svn_boolean_t clean_logs,
1303 svn_boolean_t incremental,
1304 svn_fs_hotcopy_notify_t notify_func,
1305 void *notify_baton,
1306 svn_cancel_func_t cancel_func,
1307 void *cancel_baton,
1308 svn_mutex__t *common_pool_lock,
1309 apr_pool_t *pool,
1310 apr_pool_t *common_pool)
1311 {
1312 svn_error_t *err;
1313 u_int32_t pagesize;
1314 svn_boolean_t log_autoremove = FALSE;
1315 int format;
1316
1317 if (incremental)
1318 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1319 _("BDB repositories do not support incremental "
1320 "hotcopy"));
1321
1322 /* Check the FS format number to be certain that we know how to
1323 hotcopy this FS. Pre-1.2 filesystems did not have a format file (you
1324 could say they were format "0"), so we will error here. This is not
1325 optimal, but since this has been the case since 1.2.0, and no one has
1326 complained, it apparently isn't much of a concern. (We did not check
1327 the 'format' file in 1.2.x, but we did blindly try to copy 'locks',
1328 which would have errored just the same.) */
1329 SVN_ERR(svn_io_read_version_file(
1330 &format, svn_dirent_join(src_path, FORMAT_FILE, pool), pool));
1331 SVN_ERR(check_format(format));
1332
1333 /* If using Berkeley DB 4.2 or later, note whether the DB_LOG_AUTO_REMOVE
1334 feature is on. If it is, we have a potential race condition:
1335 another process might delete a logfile while we're in the middle
1336 of copying all the logfiles. (This is not a huge deal; at worst,
1337 the hotcopy fails with a file-not-found error.) */
1338 #if SVN_BDB_VERSION_AT_LEAST(4, 2)
1339 err = check_env_flags(&log_autoremove,
1340 #if SVN_BDB_VERSION_AT_LEAST(4, 7)
1341 DB_LOG_AUTO_REMOVE,
1342 /* DB_LOG_AUTO_REMOVE was named DB_LOG_AUTOREMOVE before Berkeley DB 4.7. */
1343 #else
1344 DB_LOG_AUTOREMOVE,
1345 #endif
1346 src_path, pool);
1347 #endif
1348 SVN_ERR(err);
1349
1350 /* Copy the DB_CONFIG file. */
1351 SVN_ERR(svn_io_dir_file_copy(src_path, dest_path, "DB_CONFIG", pool));
1352
1353 /* In order to copy the database files safely and atomically, we
1354 must copy them in chunks which are multiples of the page-size
1355 used by BDB. See sleepycat docs for details, or svn issue #1818. */
1356 #if SVN_BDB_VERSION_AT_LEAST(4, 2)
1357 SVN_ERR(get_db_pagesize(&pagesize, src_path, pool));
1358 if (pagesize < SVN__STREAM_CHUNK_SIZE)
1359 {
1360 /* use the largest multiple of BDB pagesize we can. */
1361 int multiple = SVN__STREAM_CHUNK_SIZE / pagesize;
1362 pagesize *= multiple;
1363 }
1364 #else
1365 /* default to 128K chunks, which should be safe.
1366 BDB almost certainly uses a power-of-2 pagesize. */
1367 pagesize = (4096 * 32);
1368 #endif
1369
1370 /* Copy the databases. */
1371 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1372 "nodes", pagesize, FALSE, pool));
1373 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1374 "transactions", pagesize, FALSE, pool));
1375 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1376 "revisions", pagesize, FALSE, pool));
1377 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1378 "copies", pagesize, FALSE, pool));
1379 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1380 "changes", pagesize, FALSE, pool));
1381 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1382 "representations", pagesize, FALSE, pool));
1383 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1384 "strings", pagesize, FALSE, pool));
1385 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1386 "uuids", pagesize, TRUE, pool));
1387 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1388 "locks", pagesize, TRUE, pool));
1389 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1390 "lock-tokens", pagesize, TRUE, pool));
1391 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1392 "node-origins", pagesize, TRUE, pool));
1393 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1394 "checksum-reps", pagesize, TRUE, pool));
1395 SVN_ERR(copy_db_file_safely(src_path, dest_path,
1396 "miscellaneous", pagesize, TRUE, pool));
1397
1398 {
1399 apr_array_header_t *logfiles;
1400 int idx;
1401 apr_pool_t *subpool;
1402
1403 SVN_ERR(base_bdb_logfiles(&logfiles,
1404 src_path,
1405 FALSE, /* All logs */
1406 pool));
1407
1408 /* Process log files. */
1409 subpool = svn_pool_create(pool);
1410 for (idx = 0; idx < logfiles->nelts; idx++)
1411 {
1412 svn_pool_clear(subpool);
1413 err = svn_io_dir_file_copy(src_path, dest_path,
1414 APR_ARRAY_IDX(logfiles, idx,
1415 const char *),
1416 subpool);
1417 if (err)
1418 {
1419 if (log_autoremove)
1420 return
1421 svn_error_quick_wrap
1422 (err,
1423 _("Error copying logfile; the DB_LOG_AUTOREMOVE feature\n"
1424 "may be interfering with the hotcopy algorithm. If\n"
1425 "the problem persists, try deactivating this feature\n"
1426 "in DB_CONFIG"));
1427 else
1428 return svn_error_trace(err);
1429 }
1430 }
1431 svn_pool_destroy(subpool);
1432 }
1433
1434 /* Since this is a copy we will have exclusive access to the repository. */
1435 err = bdb_recover(dest_path, TRUE, pool);
1436 if (err)
1437 {
1438 if (log_autoremove)
1439 return
1440 svn_error_quick_wrap
1441 (err,
1442 _("Error running catastrophic recovery on hotcopy; the\n"
1443 "DB_LOG_AUTOREMOVE feature may be interfering with the\n"
1444 "hotcopy algorithm. If the problem persists, try deactivating\n"
1445 "this feature in DB_CONFIG"));
1446 else
1447 return svn_error_trace(err);
1448 }
1449
1450 /* Only now that the hotcopied filesystem is complete,
1451 stamp it with a format file. */
1452 SVN_ERR(svn_io_write_version_file(
1453 svn_dirent_join(dest_path, FORMAT_FILE, pool), format, pool));
1454
1455 if (clean_logs)
1456 SVN_ERR(svn_fs_base__clean_logs(src_path, dest_path, pool));
1457
1458 return SVN_NO_ERROR;
1459 }
1460
1461
1462
1463 /* Deleting a Berkeley DB-based filesystem. */
1464
1465
1466 static svn_error_t *
base_delete_fs(const char * path,apr_pool_t * pool)1467 base_delete_fs(const char *path,
1468 apr_pool_t *pool)
1469 {
1470 /* First, use the Berkeley DB library function to remove any shared
1471 memory segments. */
1472 SVN_ERR(svn_fs_bdb__remove(path, pool));
1473
1474 /* Remove the environment directory. */
1475 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool);
1476 }
1477
1478 static const svn_version_t *
base_version(void)1479 base_version(void)
1480 {
1481 SVN_VERSION_BODY;
1482 }
1483
1484 static const char *
base_get_description(void)1485 base_get_description(void)
1486 {
1487 return _("Module for working with a Berkeley DB repository.");
1488 }
1489
1490 static svn_error_t *
base_set_svn_fs_open(svn_fs_t * fs,svn_error_t * (* svn_fs_open_)(svn_fs_t **,const char *,apr_hash_t *,apr_pool_t *,apr_pool_t *))1491 base_set_svn_fs_open(svn_fs_t *fs,
1492 svn_error_t *(*svn_fs_open_)(svn_fs_t **,
1493 const char *,
1494 apr_hash_t *,
1495 apr_pool_t *,
1496 apr_pool_t *))
1497 {
1498 return SVN_NO_ERROR;
1499 }
1500
1501
1502 /* Base FS library vtable, used by the FS loader library. */
1503 static fs_library_vtable_t library_vtable = {
1504 base_version,
1505 base_create,
1506 base_open,
1507 base_open_for_recovery,
1508 base_upgrade,
1509 base_verify,
1510 base_delete_fs,
1511 base_hotcopy,
1512 base_get_description,
1513 base_bdb_recover,
1514 base_bdb_pack,
1515 base_bdb_logfiles,
1516 svn_fs_base__id_parse,
1517 base_set_svn_fs_open,
1518 NULL /* info_fsap_dup */
1519 };
1520
1521 svn_error_t *
svn_fs_base__init(const svn_version_t * loader_version,fs_library_vtable_t ** vtable,apr_pool_t * common_pool)1522 svn_fs_base__init(const svn_version_t *loader_version,
1523 fs_library_vtable_t **vtable, apr_pool_t* common_pool)
1524 {
1525 static const svn_version_checklist_t checklist[] =
1526 {
1527 { "svn_subr", svn_subr_version },
1528 { "svn_delta", svn_delta_version },
1529 { "svn_fs_util", svn_fs_util__version },
1530 { NULL, NULL }
1531 };
1532
1533 /* Simplified version check to make sure we can safely use the
1534 VTABLE parameter. The FS loader does a more exhaustive check. */
1535 if (loader_version->major != SVN_VER_MAJOR)
1536 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
1537 _("Unsupported FS loader version (%d) for bdb"),
1538 loader_version->major);
1539 SVN_ERR(svn_ver_check_list2(base_version(), checklist, svn_ver_equal));
1540 SVN_ERR(check_bdb_version());
1541 SVN_ERR(svn_fs_bdb__init(common_pool));
1542
1543 *vtable = &library_vtable;
1544 return SVN_NO_ERROR;
1545 }
1546