/* * __wt_backup_list_uri_append -- * Append a new file name to the list, allocate space as necessary. * Called via the schema_worker function. */ int __wt_backup_list_uri_append( WT_SESSION_IMPL *session, const char *name, int *skip) { WT_CURSOR_BACKUP *cb; char *value; cb = session->bkp_cursor; WT_UNUSED(skip); if (WT_PREFIX_MATCH(name, "log:")) { WT_RET(__backup_log_append(session, cb, 0)); return (0); } /* Add the metadata entry to the backup file. */ WT_RET(__wt_metadata_search(session, name, &value)); WT_RET_TEST( (fprintf(cb->bfp, "%s\n%s\n", name, value) < 0), __wt_errno()); __wt_free(session, value); /* Add file type objects to the list of files to be copied. */ if (WT_PREFIX_MATCH(name, "file:")) WT_RET(__backup_list_append(session, cb, name)); return (0); }
/* * __backup_list_all_append -- * Append a new file name to the list, allocate space as necessary. * Called via the __wt_meta_btree_apply function. */ static int __backup_list_all_append(WT_SESSION_IMPL *session, const char *cfg[]) { WT_CURSOR_BACKUP *cb; WT_UNUSED(cfg); cb = session->bkp_cursor; /* Ignore files in the process of being bulk-loaded. */ if (F_ISSET(S2BT(session), WT_BTREE_BULK)) return (0); /* Add the file to the list of files to be copied. */ return (__backup_list_append(session, cb, session->dhandle->name)); }
/* * __backup_list_uri_append -- * Append a new file name to the list, allocate space as necessary. * Called via the schema_worker function. */ static int __backup_list_uri_append( WT_SESSION_IMPL *session, const char *name, bool *skip) { WT_CURSOR_BACKUP *cb; char *value; cb = session->bkp_cursor; WT_UNUSED(skip); /* * While reading the metadata file, check there are no data sources * that can't support hot backup. This checks for a data source that's * non-standard, which can't be backed up, but is also sanity checking: * if there's an entry backed by anything other than a file or lsm * entry, we're confused. */ if (WT_PREFIX_MATCH(name, "log:")) { WT_RET(__backup_log_append(session, cb, false)); return (0); } if (!WT_PREFIX_MATCH(name, "file:") && !WT_PREFIX_MATCH(name, "colgroup:") && !WT_PREFIX_MATCH(name, "index:") && !WT_PREFIX_MATCH(name, "lsm:") && !WT_PREFIX_MATCH(name, "table:")) WT_RET_MSG(session, ENOTSUP, "hot backup is not supported for objects of type %s", name); /* Ignore the lookaside table. */ if (strcmp(name, WT_LAS_URI) == 0) return (0); /* Add the metadata entry to the backup file. */ WT_RET(__wt_metadata_search(session, name, &value)); WT_RET(__wt_fprintf(cb->bfp, "%s\n%s\n", name, value)); __wt_free(session, value); /* Add file type objects to the list of files to be copied. */ if (WT_PREFIX_MATCH(name, "file:")) WT_RET(__backup_list_append(session, cb, name)); return (0); }
/* * __backup_log_append -- * Append log files needed for backup. */ static int __backup_log_append(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, bool active) { WT_CONNECTION_IMPL *conn; WT_DECL_RET; u_int i, logcount; char **logfiles; conn = S2C(session); logfiles = NULL; logcount = 0; ret = 0; if (conn->log) { WT_ERR(__wt_log_get_all_files( session, &logfiles, &logcount, &cb->maxid, active)); for (i = 0; i < logcount; i++) WT_ERR(__backup_list_append(session, cb, logfiles[i])); } err: WT_TRET(__wt_fs_directory_list_free(session, &logfiles, logcount)); return (ret); }
/* * __backup_start -- * Start a backup. */ static int __backup_start( WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *cfg[]) { WT_CONNECTION_IMPL *conn; WT_DECL_RET; int exist, log_only, target_list; conn = S2C(session); cb->next = 0; cb->list = NULL; /* * Single thread hot backups: we're holding the schema lock, so we * know we'll serialize with other attempts to start a hot backup. */ if (conn->hot_backup) WT_RET_MSG( session, EINVAL, "there is already a backup cursor open"); /* * The hot backup copy is done outside of WiredTiger, which means file * blocks can't be freed and re-allocated until the backup completes. * The checkpoint code checks the backup flag, and if a backup cursor * is open checkpoints aren't discarded. We release the lock as soon * as we've set the flag, we don't want to block checkpoints, we just * want to make sure no checkpoints are deleted. The checkpoint code * holds the lock until it's finished the checkpoint, otherwise we * could start a hot backup that would race with an already-started * checkpoint. */ __wt_spin_lock(session, &conn->hot_backup_lock); conn->hot_backup = 1; __wt_spin_unlock(session, &conn->hot_backup_lock); /* Create the hot backup file. */ WT_ERR(__backup_file_create(session, cb, 0)); /* Add log files if logging is enabled. */ /* * If a list of targets was specified, work our way through them. * Else, generate a list of all database objects. * * Include log files if doing a full backup, and copy them before * copying data files to avoid rolling the metadata forward across * a checkpoint that completes during the backup. */ target_list = 0; WT_ERR(__backup_uri(session, cb, cfg, &target_list, &log_only)); if (!target_list) { WT_ERR(__backup_log_append(session, cb, 1)); WT_ERR(__backup_all(session, cb)); } /* Add the hot backup and standard WiredTiger files to the list. */ if (log_only) { /* * Close any hot backup file. * We're about to open the incremental backup file. */ if (cb->bfp != NULL) { WT_TRET(fclose(cb->bfp) == 0 ? 0 : __wt_errno()); cb->bfp = NULL; } WT_ERR(__backup_file_create(session, cb, log_only)); WT_ERR(__backup_list_append( session, cb, WT_INCREMENTAL_BACKUP)); } else { WT_ERR(__backup_list_append( session, cb, WT_METADATA_BACKUP)); WT_ERR(__wt_exist(session, WT_BASECONFIG, &exist)); if (exist) WT_ERR(__backup_list_append( session, cb, WT_BASECONFIG)); WT_ERR(__wt_exist(session, WT_USERCONFIG, &exist)); if (exist) WT_ERR(__backup_list_append( session, cb, WT_USERCONFIG)); WT_ERR(__backup_list_append(session, cb, WT_WIREDTIGER)); } err: /* Close the hot backup file. */ if (cb->bfp != NULL) { WT_TRET(fclose(cb->bfp) == 0 ? 0 : __wt_errno()); cb->bfp = NULL; } if (ret != 0) { WT_TRET(__backup_cleanup_handles(session, cb)); WT_TRET(__backup_stop(session)); } return (ret); }
/* * __backup_start -- * Start a backup. */ static int __backup_start( WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *cfg[]) { WT_CONNECTION_IMPL *conn; WT_DECL_RET; WT_FSTREAM *srcfs; const char *dest; bool exist, log_only, target_list; conn = S2C(session); srcfs = NULL; dest = NULL; cb->next = 0; cb->list = NULL; cb->list_next = 0; WT_RET(__wt_inmem_unsupported_op(session, "backup cursor")); /* * Single thread hot backups: we're holding the schema lock, so we * know we'll serialize with other attempts to start a hot backup. */ if (conn->hot_backup) WT_RET_MSG( session, EINVAL, "there is already a backup cursor open"); /* * The hot backup copy is done outside of WiredTiger, which means file * blocks can't be freed and re-allocated until the backup completes. * The checkpoint code checks the backup flag, and if a backup cursor * is open checkpoints aren't discarded. We release the lock as soon * as we've set the flag, we don't want to block checkpoints, we just * want to make sure no checkpoints are deleted. The checkpoint code * holds the lock until it's finished the checkpoint, otherwise we * could start a hot backup that would race with an already-started * checkpoint. * * We are holding the checkpoint and schema locks so schema operations * will not see the backup file list until it is complete and valid. */ __wt_writelock(session, &conn->hot_backup_lock); conn->hot_backup = true; conn->hot_backup_list = NULL; __wt_writeunlock(session, &conn->hot_backup_lock); /* We're the lock holder, we own cleanup. */ F_SET(cb, WT_CURBACKUP_LOCKER); /* * Create a temporary backup file. This must be opened before * generating the list of targets in backup_uri. This file will * later be renamed to the correct name depending on whether or not * we're doing an incremental backup. We need a temp file so that if * we fail or crash while filling it, the existence of a partial file * doesn't confuse restarting in the source database. */ WT_ERR(__wt_fopen(session, WT_BACKUP_TMP, WT_FS_OPEN_CREATE, WT_STREAM_WRITE, &cb->bfs)); /* * If a list of targets was specified, work our way through them. * Else, generate a list of all database objects. * * Include log files if doing a full backup, and copy them before * copying data files to avoid rolling the metadata forward across * a checkpoint that completes during the backup. */ target_list = false; WT_ERR(__backup_uri(session, cfg, &target_list, &log_only)); if (!target_list) { WT_ERR(__backup_log_append(session, cb, true)); WT_ERR(__backup_all(session)); } /* Add the hot backup and standard WiredTiger files to the list. */ if (log_only) { /* * We also open an incremental backup source file so that we * can detect a crash with an incremental backup existing in * the source directory versus an improper destination. */ dest = WT_INCREMENTAL_BACKUP; WT_ERR(__wt_fopen(session, WT_INCREMENTAL_SRC, WT_FS_OPEN_CREATE, WT_STREAM_WRITE, &srcfs)); WT_ERR(__backup_list_append( session, cb, WT_INCREMENTAL_BACKUP)); } else { dest = WT_METADATA_BACKUP; WT_ERR(__backup_list_append(session, cb, WT_METADATA_BACKUP)); WT_ERR(__wt_fs_exist(session, WT_BASECONFIG, &exist)); if (exist) WT_ERR(__backup_list_append( session, cb, WT_BASECONFIG)); WT_ERR(__wt_fs_exist(session, WT_USERCONFIG, &exist)); if (exist) WT_ERR(__backup_list_append( session, cb, WT_USERCONFIG)); WT_ERR(__backup_list_append(session, cb, WT_WIREDTIGER)); } err: /* Close the hot backup file. */ WT_TRET(__wt_fclose(session, &cb->bfs)); if (srcfs != NULL) WT_TRET(__wt_fclose(session, &srcfs)); if (ret == 0) { WT_ASSERT(session, dest != NULL); WT_TRET(__wt_fs_rename(session, WT_BACKUP_TMP, dest, false)); __wt_writelock(session, &conn->hot_backup_lock); conn->hot_backup_list = cb->list; __wt_writeunlock(session, &conn->hot_backup_lock); } return (ret); }