1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996,2008 Oracle. All rights reserved. 5 * 6 * $Id: db_hotbackup.c,v 1.59 2008/01/31 18:40:42 bostic Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12#include "dbinc/log.h" 13#include "dbinc/db_page.h" 14#include "dbinc/qam.h" 15 16#ifndef lint 17static const char copyright[] = 18 "Copyright (c) 1996,2008 Oracle. All rights reserved.\n"; 19#endif 20 21enum which_open { OPEN_ORIGINAL, OPEN_HOT_BACKUP }; 22 23int db_hotbackup_backup_dir_clean __P((DB_ENV *, char *, char *, int *, int, int)); 24int db_hotbackup_data_copy __P((DB_ENV *, char *, char *, char *, int)); 25int db_hotbackup_env_init __P((DB_ENV **, 26 char *, char **, char ***, char *, enum which_open)); 27int db_hotbackup_main __P((int, char *[])); 28int db_hotbackup_read_data_dir __P((DB_ENV *, char *, char *, char *, int, int)); 29int db_hotbackup_read_log_dir __P((DB_ENV *, char *, char *, char *, int *, int, int)); 30int db_hotbackup_usage __P((void)); 31int db_hotbackup_version_check __P((void)); 32 33const char *progname; 34 35int 36db_hotbackup(args) 37 char *args; 38{ 39 int argc; 40 char **argv; 41 42 __db_util_arg("db_hotbackup", args, &argc, &argv); 43 return (db_hotbackup_main(argc, argv) ? EXIT_FAILURE : EXIT_SUCCESS); 44} 45 46#include <stdio.h> 47#define ERROR_RETURN ERROR 48 49int 50db_hotbackup_main(argc, argv) 51 int argc; 52 char *argv[]; 53{ 54 extern char *optarg; 55 extern int optind, __db_getopt_reset; 56 time_t now; 57 DB_ENV *dbenv; 58 u_int data_cnt, data_next; 59 int ch, checkpoint, copy_min, db_config, exitval; 60 int remove_max, ret, update, verbose; 61 char *backup_dir, **data_dir, **dir, *home, *log_dir, *passwd; 62 char home_buf[DB_MAXPATHLEN], time_buf[CTIME_BUFLEN]; 63 64 /* 65 * Make sure all verbose message are output before any error messages 66 * in the case where the output is being logged into a file. This 67 * call has to be done before any operation is performed on the stream. 68 * 69 * Use unbuffered I/O because line-buffered I/O requires a buffer, and 70 * some operating systems have buffer alignment and size constraints we 71 * don't want to care about. There isn't enough output for the calls 72 * to matter. 73 */ 74 setbuf(stdout, NULL); 75 76 if ((progname = __db_rpath(argv[0])) == NULL) 77 progname = argv[0]; 78 else 79 ++progname; 80 81 if ((ret = db_hotbackup_version_check()) != 0) 82 return (ret); 83 84 checkpoint = db_config = data_cnt = 85 data_next = exitval = update = verbose = 0; 86 data_dir = NULL; 87 backup_dir = home = passwd = NULL; 88 log_dir = NULL; 89 copy_min = remove_max = 0; 90 __db_getopt_reset = 1; 91 while ((ch = getopt(argc, argv, "b:cDd:h:l:P:uVv")) != EOF) 92 switch (ch) { 93 case 'b': 94 backup_dir = optarg; 95 break; 96 case 'c': 97 checkpoint = 1; 98 break; 99 case 'D': 100 db_config = 1; 101 break; 102 case 'd': 103 /* 104 * User can specify a list of directories -- keep an 105 * array, leaving room for the trailing NULL. 106 */ 107 if (data_dir == NULL || data_next >= data_cnt - 2) { 108 data_cnt = data_cnt == 0 ? 20 : data_cnt * 2; 109 if ((data_dir = realloc(data_dir, 110 data_cnt * sizeof(*data_dir))) == NULL) { 111 fprintf(stderr, "%s: %s\n", 112 progname, strerror(errno)); 113 return (EXIT_FAILURE); 114 } 115 } 116 data_dir[data_next++] = optarg; 117 break; 118 case 'h': 119 home = optarg; 120 break; 121 case 'l': 122 log_dir = optarg; 123 break; 124 case 'P': 125 passwd = strdup(optarg); 126 memset(optarg, 0, strlen(optarg)); 127 if (passwd == NULL) { 128 fprintf(stderr, "%s: strdup: %s\n", 129 progname, strerror(errno)); 130 return (EXIT_FAILURE); 131 } 132 break; 133 case 'u': 134 update = 1; 135 break; 136 case 'V': 137 printf("%s\n", db_version(NULL, NULL, NULL)); 138 return (EXIT_SUCCESS); 139 case 'v': 140 verbose = 1; 141 break; 142 case '?': 143 default: 144 return (db_hotbackup_usage()); 145 } 146 argc -= optind; 147 argv += optind; 148 149 if (argc != 0) 150 return (db_hotbackup_usage()); 151 152 /* NULL-terminate any list of data directories. */ 153 if (data_dir != NULL) { 154 data_dir[data_next] = NULL; 155 /* 156 * -d is relative to the current directory, to run a checkpoint 157 * we must have directories relative to the environment. 158 */ 159 if (checkpoint == 1) { 160 fprintf(stderr, 161 "%s: cannot specify -d and -c\n", progname); 162 return (db_hotbackup_usage()); 163 } 164 } 165 166 if (db_config && (data_dir != NULL || log_dir != NULL)) { 167 fprintf(stderr, 168 "%s: cannot specify -D and -d or -l\n", progname); 169 return (db_hotbackup_usage()); 170 } 171 172 /* Handle possible interruptions. */ 173 __db_util_siginit(); 174 175 /* 176 * The home directory defaults to the environment variable DB_HOME. 177 * The log directory defaults to the home directory. 178 * 179 * We require a source database environment directory and a target 180 * backup directory. 181 */ 182 if (home == NULL) { 183 home = home_buf; 184 if ((ret = __os_getenv( 185 NULL, "DB_HOME", &home, sizeof(home_buf))) != 0) { 186 fprintf(stderr, 187 "%s failed to get environment variable DB_HOME: %s\n", 188 progname, db_strerror(ret)); 189 return (EXIT_FAILURE); 190 } 191 /* 192 * home set to NULL if __os_getenv failed to find DB_HOME. 193 */ 194 } 195 if (home == NULL) { 196 fprintf(stderr, 197 "%s: no source database environment specified\n", progname); 198 return (db_hotbackup_usage()); 199 } 200 if (backup_dir == NULL) { 201 fprintf(stderr, 202 "%s: no target backup directory specified\n", progname); 203 return (db_hotbackup_usage()); 204 } 205 206 if (verbose) { 207 (void)time(&now); 208 printf("%s: hot backup started at %s", 209 progname, __os_ctime(&now, time_buf)); 210 } 211 212 /* Open the source environment. */ 213 if (db_hotbackup_env_init(&dbenv, home, 214 (db_config || log_dir != NULL) ? &log_dir : NULL, 215 db_config ? &data_dir : NULL, 216 passwd, OPEN_ORIGINAL) != 0) 217 goto shutdown; 218 219 if (db_config && __os_abspath(log_dir)) { 220 fprintf(stderr, 221 "%s: DB_CONFIG must not contain an absolute path for the log directory", 222 progname); 223 goto shutdown; 224 } 225 226 /* 227 * If the -c option is specified, checkpoint the source home 228 * database environment, and remove any unnecessary log files. 229 */ 230 if (checkpoint) { 231 if (verbose) 232 printf("%s: %s: force checkpoint\n", progname, home); 233 if ((ret = 234 dbenv->txn_checkpoint(dbenv, 0, 0, DB_FORCE)) != 0) { 235 dbenv->err(dbenv, ret, "DB_ENV->txn_checkpoint"); 236 goto shutdown; 237 } 238 if (!update) { 239 if (verbose) 240 printf("%s: %s: remove unnecessary log files\n", 241 progname, home); 242 if ((ret = dbenv->log_archive(dbenv, 243 NULL, DB_ARCH_REMOVE)) != 0) { 244 dbenv->err(dbenv, ret, "DB_ENV->log_archive"); 245 goto shutdown; 246 } 247 } 248 } 249 250 /* 251 * If the target directory for the backup does not exist, create it 252 * with mode read-write-execute for the owner. Ignore errors here, 253 * it's simpler and more portable to just always try the create. If 254 * there's a problem, we'll fail with reasonable errors later. 255 */ 256 (void)__os_mkdir(NULL, backup_dir, DB_MODE_700); 257 258 /* 259 * If -u was specified, remove all log files; if -u was not specified, 260 * remove all files. 261 * 262 * Potentially there are two directories to clean, the log directory 263 * and the target directory. First, clean up the log directory if 264 * it's different from the target directory, then clean up the target 265 * directory. 266 */ 267 if (db_config && log_dir != NULL && 268 db_hotbackup_backup_dir_clean( 269 dbenv, backup_dir, log_dir, &remove_max, update, verbose) != 0) 270 goto shutdown; 271 if (db_hotbackup_backup_dir_clean(dbenv, 272 backup_dir, NULL, &remove_max, update, verbose) != 0) 273 goto shutdown; 274 275 /* 276 * If the -u option was not specified, copy all database files found in 277 * the database environment home directory, or any directory specified 278 * using the -d option, into the target directory for the backup. 279 */ 280 if (!update) { 281 if (db_hotbackup_read_data_dir(dbenv, home, 282 backup_dir, home, verbose, db_config) != 0) 283 goto shutdown; 284 if (data_dir != NULL) 285 for (dir = data_dir; *dir != NULL; ++dir) { 286 /* 287 * Don't allow absolute path names taken from 288 * the DB_CONFIG file -- running recovery with 289 * them would corrupt the source files. 290 */ 291 if (db_config && __os_abspath(*dir)) { 292 fprintf(stderr, 293 "%s: data directory '%s' is absolute path, not permitted with -D option\n", 294 progname, *dir); 295 goto shutdown; 296 } 297 if (db_hotbackup_read_data_dir(dbenv, home, 298 backup_dir, *dir, verbose, db_config) != 0) 299 goto shutdown; 300 } 301 } 302 303 /* 304 * Copy all log files found in the directory specified by the -l option 305 * (or in the database environment home directory, if no -l option was 306 * specified), into the target directory for the backup. 307 * 308 * The log directory defaults to the home directory. 309 */ 310 if (db_hotbackup_read_log_dir(dbenv, db_config ? home : NULL, backup_dir, 311 log_dir == NULL ? home : log_dir, ©_min, update, verbose) != 0) 312 goto shutdown; 313 314 /* 315 * If we're updating a snapshot, the lowest-numbered log file copied 316 * into the backup directory should be less than, or equal to, the 317 * highest-numbered log file removed from the backup directory during 318 * cleanup. 319 */ 320 if (update && remove_max < copy_min && 321 !(remove_max == 0 && copy_min == 1)) { 322 fprintf(stderr, 323 "%s: the largest log file removed (%d) must be greater\n", 324 progname, remove_max); 325 fprintf(stderr, 326 "%s: than or equal the smallest log file copied (%d)\n", 327 progname, copy_min); 328 goto shutdown; 329 } 330 331 /* Close the source environment. */ 332 if ((ret = dbenv->close(dbenv, 0)) != 0) { 333 fprintf(stderr, 334 "%s: dbenv->close: %s\n", progname, db_strerror(ret)); 335 dbenv = NULL; 336 goto shutdown; 337 } 338 /* Perform catastrophic recovery on the hot backup. */ 339 if (verbose) 340 printf("%s: %s: run catastrophic recovery\n", 341 progname, backup_dir); 342 if (db_hotbackup_env_init( 343 &dbenv, backup_dir, NULL, NULL, passwd, OPEN_HOT_BACKUP) != 0) 344 goto shutdown; 345 346 /* 347 * Remove any unnecessary log files from the hot backup. 348 */ 349 if (verbose) 350 printf("%s: %s: remove unnecessary log files\n", 351 progname, backup_dir); 352 if ((ret = 353 dbenv->log_archive(dbenv, NULL, DB_ARCH_REMOVE)) != 0) { 354 dbenv->err(dbenv, ret, "DB_ENV->log_archive"); 355 goto shutdown; 356 } 357 358 if (0) { 359shutdown: exitval = 1; 360 } 361 if (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0) { 362 exitval = 1; 363 fprintf(stderr, 364 "%s: dbenv->close: %s\n", progname, db_strerror(ret)); 365 } 366 367 if (exitval == 0) { 368 if (verbose) { 369 (void)time(&now); 370 printf("%s: hot backup completed at %s", 371 progname, __os_ctime(&now, time_buf)); 372 } 373 } else { 374 fprintf(stderr, "%s: HOT BACKUP FAILED!\n", progname); 375 } 376 377 /* Resend any caught signal. */ 378 __db_util_sigresend(); 379 380 return (exitval == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 381 382} 383 384/* 385 * env_init -- 386 * Open a database environment. 387 */ 388int 389db_hotbackup_env_init(dbenvp, home, log_dirp, data_dirp, passwd, which) 390 DB_ENV **dbenvp; 391 char *home, **log_dirp, ***data_dirp, *passwd; 392 enum which_open which; 393{ 394 DB_ENV *dbenv; 395 int ret; 396 397 *dbenvp = NULL; 398 399 /* 400 * Create an environment object and initialize it for error reporting. 401 */ 402 if ((ret = db_env_create(&dbenv, 0)) != 0) { 403 fprintf(stderr, 404 "%s: db_env_create: %s\n", progname, db_strerror(ret)); 405 return (1); 406 } 407 408 dbenv->set_errfile(dbenv, stderr); 409 setbuf(stderr, NULL); 410 dbenv->set_errpfx(dbenv, progname); 411 412 /* Any created intermediate directories are created private. */ 413 if ((ret = dbenv->set_intermediate_dir_mode(dbenv, "rwx------")) != 0) { 414 dbenv->err(dbenv, ret, "DB_ENV->set_intermediate_dir_mode"); 415 return (1); 416 } 417 418 /* 419 * If a log directory has been specified, and it's not the same as the 420 * home directory, set it for the environment. 421 */ 422 if (log_dirp != NULL && *log_dirp != NULL && 423 (ret = dbenv->set_lg_dir(dbenv, *log_dirp)) != 0) { 424 dbenv->err(dbenv, ret, "DB_ENV->set_lg_dir: %s", *log_dirp); 425 return (1); 426 } 427 428 /* Optionally set the password. */ 429 if (passwd != NULL && 430 (ret = dbenv->set_encrypt(dbenv, passwd, DB_ENCRYPT_AES)) != 0) { 431 dbenv->err(dbenv, ret, "DB_ENV->set_encrypt"); 432 return (1); 433 } 434 435 switch (which) { 436 case OPEN_ORIGINAL: 437 /* 438 * Opening the database environment we're trying to back up. 439 * We try to attach to a pre-existing environment; if that 440 * fails, we create a private environment and try again. 441 */ 442 if ((ret = dbenv->open(dbenv, home, DB_USE_ENVIRON, 0)) != 0 && 443 (ret == DB_VERSION_MISMATCH || 444 (ret = dbenv->open(dbenv, home, DB_CREATE | 445 DB_INIT_LOG | DB_INIT_TXN | DB_PRIVATE | DB_USE_ENVIRON, 446 0)) != 0)) { 447 dbenv->err(dbenv, ret, "DB_ENV->open: %s", home); 448 return (1); 449 } 450 if (log_dirp != NULL && *log_dirp == NULL) 451 (void)dbenv->get_lg_dir(dbenv, (const char **)log_dirp); 452 if (data_dirp != NULL && *data_dirp == NULL) 453 (void)dbenv->get_data_dirs( 454 dbenv, (const char ***)data_dirp); 455 break; 456 case OPEN_HOT_BACKUP: 457 /* 458 * Opening the backup copy of the database environment. We 459 * better be the only user, we're running recovery. 460 * Ensure that there at least minimal cache for worst 461 * case page size. 462 */ 463 if ((ret = 464 dbenv->set_cachesize(dbenv, 0, 64 * 1024 * 10, 0)) != 0) { 465 dbenv->err(dbenv, 466 ret, "DB_ENV->set_cachesize: %s", home); 467 return (1); 468 } 469 if ((ret = dbenv->open(dbenv, home, DB_CREATE | 470 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_PRIVATE | 471 DB_RECOVER_FATAL | DB_USE_ENVIRON, 0)) != 0) { 472 dbenv->err(dbenv, ret, "DB_ENV->open: %s", home); 473 return (1); 474 } 475 break; 476 } 477 478 *dbenvp = dbenv; 479 return (0); 480} 481 482/* 483 * backup_dir_clean -- 484 * Clean out the backup directory. 485 */ 486int 487db_hotbackup_backup_dir_clean(dbenv, backup_dir, log_dir, remove_maxp, update, verbose) 488 DB_ENV *dbenv; 489 char *backup_dir, *log_dir; 490 int *remove_maxp, update, verbose; 491{ 492 ENV *env; 493 int cnt, fcnt, ret, v; 494 char **names, *dir, buf[DB_MAXPATHLEN], path[DB_MAXPATHLEN]; 495 496 env = dbenv->env; 497 498 /* We may be cleaning a log directory separate from the target. */ 499 if (log_dir != NULL) { 500 if ((size_t)snprintf(buf, sizeof(buf), "%s%c%s", 501 backup_dir, PATH_SEPARATOR[0] ,log_dir) >= sizeof(buf)) { 502 dbenv->errx(dbenv, "%s%c%s: path too long", 503 backup_dir, PATH_SEPARATOR[0] ,log_dir); 504 return (1); 505 } 506 dir = buf; 507 } else 508 dir = backup_dir; 509 510 /* Get a list of file names. */ 511 if ((ret = __os_dirlist(env, dir, 0, &names, &fcnt)) != 0) { 512 if (log_dir != NULL && !update) 513 return (0); 514 dbenv->err(dbenv, ret, "%s: directory read", dir); 515 return (1); 516 } 517 for (cnt = fcnt; --cnt >= 0;) { 518 /* 519 * Skip non-log files (if update was specified). 520 */ 521 if (strncmp(names[cnt], LFPREFIX, sizeof(LFPREFIX) - 1)) { 522 if (update) 523 continue; 524 } else { 525 /* Track the highest-numbered log file removed. */ 526 v = atoi(names[cnt] + sizeof(LFPREFIX) - 1); 527 if (*remove_maxp < v) 528 *remove_maxp = v; 529 } 530 if ((size_t)snprintf(path, sizeof(path), "%s%c%s", 531 dir, PATH_SEPARATOR[0], names[cnt]) >= sizeof(path)) { 532 dbenv->errx(dbenv, "%s%c%s: path too long", 533 dir, PATH_SEPARATOR[0], names[cnt]); 534 return (1); 535 } 536 if (verbose) 537 printf("%s: removing %s\n", progname, path); 538 if (__os_unlink(env, path, 0) != 0) 539 return (1); 540 } 541 542 __os_dirfree(env, names, fcnt); 543 544 if (verbose && *remove_maxp != 0) 545 printf("%s: highest numbered log file removed: %d\n", 546 progname, *remove_maxp); 547 548 return (0); 549} 550 551/* 552 * read_data_dir -- 553 * Read a directory looking for databases to copy. 554 */ 555int 556db_hotbackup_read_data_dir(dbenv, home, backup_dir, dir, verbose, db_config) 557 DB_ENV *dbenv; 558 char *home, *backup_dir, *dir; 559 int verbose, db_config; 560{ 561 ENV *env; 562 int cnt, fcnt, ret; 563 char *bd, **names; 564 char buf[DB_MAXPATHLEN], bbuf[DB_MAXPATHLEN]; 565 566 env = dbenv->env; 567 568 bd = backup_dir; 569 if (db_config && dir != home) { 570 /* Build a path name to the destination. */ 571 if ((size_t)(cnt = snprintf(bbuf, sizeof(bbuf), "%s%c%s%c", 572 backup_dir, PATH_SEPARATOR[0], 573 dir, PATH_SEPARATOR[0])) >= sizeof(buf)) { 574 dbenv->errx(dbenv, "%s%c%s: path too long", 575 backup_dir, PATH_SEPARATOR[0], dir); 576 return (1); 577 } 578 bd = bbuf; 579 580 /* Create the path. */ 581 if ((ret = __db_mkpath(env, bd)) != 0) { 582 dbenv->err(dbenv, ret, "%s: cannot create", bd); 583 return (1); 584 } 585 /* step on the trailing '/' */ 586 bd[cnt - 1] = '\0'; 587 588 /* Build a path name to the source. */ 589 if ((size_t)snprintf(buf, sizeof(buf), 590 "%s%c%s", home, PATH_SEPARATOR[0], dir) >= sizeof(buf)) { 591 dbenv->errx(dbenv, "%s%c%s: path too long", 592 home, PATH_SEPARATOR[0], dir); 593 return (1); 594 } 595 dir = buf; 596 } 597 /* Get a list of file names. */ 598 if ((ret = __os_dirlist(env, dir, 0, &names, &fcnt)) != 0) { 599 dbenv->err(dbenv, ret, "%s: directory read", dir); 600 return (1); 601 } 602 for (cnt = fcnt; --cnt >= 0;) { 603 /* 604 * Skip files in DB's name space (but not Queue 605 * extent files, we need them). 606 */ 607 if (!strncmp(names[cnt], LFPREFIX, sizeof(LFPREFIX) - 1)) 608 continue; 609 if (!strncmp(names[cnt], 610 DB_REGION_PREFIX, sizeof(DB_REGION_PREFIX) - 1) && 611 strncmp(names[cnt], 612 QUEUE_EXTENT_PREFIX, sizeof(QUEUE_EXTENT_PREFIX) - 1)) 613 continue; 614 615 /* 616 * Skip DB_CONFIG. 617 */ 618 if (!db_config && 619 !strncmp(names[cnt], "DB_CONFIG", sizeof("DB_CONFIG"))) 620 continue; 621 622 /* Copy the file. */ 623 if (db_hotbackup_data_copy(dbenv, names[cnt], dir, bd, verbose) != 0) 624 return (1); 625 } 626 627 __os_dirfree(env, names, fcnt); 628 629 return (0); 630} 631 632/* 633 * read_log_dir -- 634 * * Read a directory looking for log files to copy. If home 635 * is passed then we are possibly using a log dir in the destination, 636 * following DB_CONFIG configuration. 637 */ 638int 639db_hotbackup_read_log_dir(dbenv, home, backup_dir, log_dir, copy_minp, update, verbose) 640 DB_ENV *dbenv; 641 char *home, *backup_dir, *log_dir; 642 int *copy_minp, update, verbose; 643{ 644 ENV *env; 645 u_int32_t aflag; 646 int cnt, ret, v; 647 char **begin, **names, *backupd, *logd; 648 char from[DB_MAXPATHLEN], to[DB_MAXPATHLEN]; 649 650 env = dbenv->env; 651 652 if (home != NULL && log_dir != NULL) { 653 if ((size_t)snprintf(from, sizeof(from), "%s%c%s", 654 home, PATH_SEPARATOR[0], log_dir) >= sizeof(from)) { 655 dbenv->errx(dbenv, "%s%c%s: path too long", 656 home, PATH_SEPARATOR[0], log_dir); 657 return (1); 658 } 659 logd = strdup(from); 660 if ((size_t)(cnt = snprintf(to, sizeof(to), 661 "%s%c%s%c", backup_dir, PATH_SEPARATOR[0], 662 log_dir, PATH_SEPARATOR[0])) >= sizeof(to)) { 663 dbenv->errx(dbenv, "%s%c%s: path too long", 664 backup_dir, PATH_SEPARATOR[0], log_dir); 665 return (1); 666 } 667 backupd = strdup(to); 668 669 /* Create the backup log directory. */ 670 if ((ret = __db_mkpath(env, backupd)) != 0) { 671 dbenv->err(dbenv, ret, "%s: cannot create", backupd); 672 return (1); 673 } 674 /* Step on the trailing '/'. */ 675 backupd[cnt - 1] = '\0'; 676 } else { 677 backupd = backup_dir; 678 logd = log_dir; 679 } 680 681again: aflag = DB_ARCH_LOG; 682 683 /* 684 * If this is an update and we are deleting files, first process 685 * those files that can be removed, then repeat with the rest. 686 */ 687 if (update) 688 aflag = 0; 689 /* Get a list of file names to be copied. */ 690 if ((ret = dbenv->log_archive(dbenv, &names, aflag)) != 0) { 691 dbenv->err(dbenv, ret, "DB_ENV->log_archive"); 692 return (1); 693 } 694 if (names == NULL) 695 goto done; 696 begin = names; 697 for (; *names != NULL; names++) { 698 /* Track the lowest-numbered log file copied. */ 699 v = atoi(*names + sizeof(LFPREFIX) - 1); 700 if (*copy_minp == 0 || *copy_minp > v) 701 *copy_minp = v; 702 703 if ((size_t)snprintf(from, sizeof(from), "%s%c%s", 704 logd, PATH_SEPARATOR[0], *names) >= sizeof(from)) { 705 dbenv->errx(dbenv, "%s%c%s: path too long", 706 logd, PATH_SEPARATOR[0], *names); 707 return (1); 708 } 709 710 /* 711 * If we're going to remove the file, attempt to rename the 712 * instead of copying and then removing. The likely failure 713 * is EXDEV (source and destination are on different volumes). 714 * Fall back to a copy, regardless of the error. We don't 715 * worry about partial contents, the copy truncates the file 716 * on open. 717 */ 718 if (update) { 719 if ((size_t)snprintf(to, sizeof(to), "%s%c%s", 720 backupd, PATH_SEPARATOR[0], *names) >= sizeof(to)) { 721 dbenv->errx(dbenv, "%s%c%s: path too long", 722 backupd, PATH_SEPARATOR[0], *names); 723 return (1); 724 } 725 if (__os_rename(env, from, to, 1) == 0) { 726 if (verbose) 727 printf("%s: moving %s to %s\n", 728 progname, from, to); 729 continue; 730 } 731 } 732 733 /* Copy the file. */ 734 if (db_hotbackup_data_copy(dbenv, *names, logd, backupd, verbose) != 0) 735 return (1); 736 737 if (update) { 738 if (verbose) 739 printf("%s: removing %s\n", progname, from); 740 if ((ret = __os_unlink(env, from, 0)) != 0) { 741 dbenv->err(dbenv, ret, 742 "unlink of %s failed", from); 743 return (1); 744 } 745 } 746 747 } 748 749 free(begin); 750done: if (update) { 751 update = 0; 752 goto again; 753 } 754 755 if (verbose && *copy_minp != 0) 756 printf("%s: lowest numbered log file copied: %d\n", 757 progname, *copy_minp); 758 if (logd != log_dir) 759 free(logd); 760 if (backupd != backup_dir) 761 free(backupd); 762 763 return (0); 764} 765 766/* 767 * data_copy -- 768 * Copy a file into the backup directory. 769 */ 770int 771db_hotbackup_data_copy(dbenv, file, from_dir, to_dir, verbose) 772 DB_ENV *dbenv; 773 char *file, *from_dir, *to_dir; 774 int verbose; 775{ 776 DB_FH *rfhp, *wfhp; 777 ENV *env; 778 size_t nr, nw; 779 int ret; 780 char *buf; 781 782 rfhp = wfhp = NULL; 783 env = dbenv->env; 784 ret = 0; 785 786 if (verbose) 787 printf("%s: copying %s%c%s to %s%c%s\n", progname, from_dir, 788 PATH_SEPARATOR[0], file, to_dir, PATH_SEPARATOR[0], file); 789 790 /* 791 * We MUST copy multiples of the page size, atomically, to ensure a 792 * database page is not updated by another thread of control during 793 * the copy. 794 * 795 * !!! 796 * The current maximum page size for Berkeley DB is 64KB; we will have 797 * to increase this value if the maximum page size is ever more than a 798 * megabyte 799 */ 800 if ((buf = malloc(MEGABYTE)) == NULL) { 801 dbenv->err(dbenv, 802 errno, "%lu buffer allocation", (u_long)MEGABYTE); 803 return (1); 804 } 805 806 /* Open the input file. */ 807 if (snprintf(buf, MEGABYTE, "%s%c%s", 808 from_dir, PATH_SEPARATOR[0], file) >= MEGABYTE) { 809 dbenv->errx(dbenv, 810 "%s%c%s: path too long", from_dir, PATH_SEPARATOR[0], file); 811 goto err; 812 } 813 if ((ret = __os_open(env, buf, 0, DB_OSO_RDONLY, 0, &rfhp)) != 0) { 814 dbenv->err(dbenv, ret, "%s", buf); 815 goto err; 816 } 817 818 /* Open the output file. */ 819 if (snprintf(buf, MEGABYTE, "%s%c%s", 820 to_dir, PATH_SEPARATOR[0], file) >= MEGABYTE) { 821 dbenv->errx(dbenv, 822 "%s%c%s: path too long", to_dir, PATH_SEPARATOR[0], file); 823 goto err; 824 } 825 if ((ret = __os_open(env, buf, 0, 826 DB_OSO_CREATE | DB_OSO_TRUNC, DB_MODE_600, &wfhp)) != 0) { 827 dbenv->err(dbenv, ret, "%s", buf); 828 goto err; 829 } 830 831 /* Copy the data. */ 832 while ((ret = __os_read(env, rfhp, buf, MEGABYTE, &nr)) == 0 && 833 nr > 0) 834 if ((ret = __os_write(env, wfhp, buf, nr, &nw)) != 0) 835 break; 836 837 if (0) { 838err: ret = 1; 839 } 840 if (buf != NULL) 841 free(buf); 842 843 if (rfhp != NULL && __os_closehandle(env, rfhp) != 0) 844 ret = 1; 845 846 /* We may be running on a remote filesystem; force the flush. */ 847 if (wfhp != NULL) { 848 if (__os_fsync(env, wfhp) != 0) 849 ret = 1; 850 if (__os_closehandle(env, wfhp) != 0) 851 ret = 1; 852 } 853 return (ret); 854} 855 856int 857db_hotbackup_usage() 858{ 859 (void)fprintf(stderr, "usage: %s [-cDuVv]\n\t%s\n", progname, 860 "[-d data_dir ...] [-h home] [-l log_dir] [-P password] -b backup_dir"); 861 return (EXIT_FAILURE); 862} 863 864int 865db_hotbackup_version_check() 866{ 867 int v_major, v_minor, v_patch; 868 869 /* Make sure we're loaded with the right version of the DB library. */ 870 (void)db_version(&v_major, &v_minor, &v_patch); 871 if (v_major != DB_VERSION_MAJOR || v_minor != DB_VERSION_MINOR) { 872 fprintf(stderr, 873 "%s: version %d.%d doesn't match library version %d.%d\n", 874 progname, DB_VERSION_MAJOR, DB_VERSION_MINOR, 875 v_major, v_minor); 876 return (EXIT_FAILURE); 877 } 878 return (0); 879} 880