/* * __rename_file -- * WT_SESSION::rename for a file. */ static int __rename_file( WT_SESSION_IMPL *session, const char *uri, const char *newuri) { WT_DECL_RET; bool exist; const char *filename, *newfile; char *newvalue, *oldvalue; newvalue = oldvalue = NULL; filename = uri; newfile = newuri; if (!WT_PREFIX_SKIP(filename, "file:") || !WT_PREFIX_SKIP(newfile, "file:")) return (EINVAL); /* Close any btree handles in the file. */ WT_WITH_HANDLE_LIST_LOCK(session, ret = __wt_conn_dhandle_close_all(session, uri, false)); WT_ERR(ret); /* * First, check if the file being renamed exists in the system. Doing * this check first matches the table rename behavior because we return * WT_NOTFOUND when the renamed file doesn't exist (subsequently mapped * to ENOENT by the session layer). */ WT_ERR(__wt_metadata_search(session, uri, &oldvalue)); /* * Check to see if the proposed name is already in use, in either the * metadata or the filesystem. */ switch (ret = __wt_metadata_search(session, newuri, &newvalue)) { case 0: WT_ERR_MSG(session, EEXIST, "%s", newuri); /* NOTREACHED */ case WT_NOTFOUND: break; default: WT_ERR(ret); } WT_ERR(__wt_fs_exist(session, newfile, &exist)); if (exist) WT_ERR_MSG(session, EEXIST, "%s", newfile); /* Replace the old file entries with new file entries. */ WT_ERR(__wt_metadata_remove(session, uri)); WT_ERR(__wt_metadata_insert(session, newuri, oldvalue)); /* Rename the underlying file. */ WT_ERR(__wt_fs_rename(session, filename, newfile, false)); if (WT_META_TRACKING(session)) WT_ERR(__wt_meta_track_fileop(session, uri, newuri)); err: __wt_free(session, newvalue); __wt_free(session, oldvalue); return (ret); }
/* * __wt_remove_if_exists -- * Remove a file if it exists. */ int __wt_remove_if_exists(WT_SESSION_IMPL *session, const char *name, bool durable) { bool exist; WT_RET(__wt_fs_exist(session, name, &exist)); if (exist) WT_RET(__wt_fs_remove(session, name, durable)); return (0); }
/* * __wt_turtle_read -- * Read the turtle file. */ int __wt_turtle_read(WT_SESSION_IMPL *session, const char *key, char **valuep) { WT_DECL_ITEM(buf); WT_DECL_RET; WT_FSTREAM *fs; bool exist, match; *valuep = NULL; /* * Open the turtle file; there's one case where we won't find the turtle * file, yet still succeed. We create the metadata file before creating * the turtle file, and that means returning the default configuration * string for the metadata file. */ WT_RET(__wt_fs_exist(session, WT_METADATA_TURTLE, &exist)); if (!exist) return (strcmp(key, WT_METAFILE_URI) == 0 ? __metadata_config(session, valuep) : WT_NOTFOUND); WT_RET(__wt_fopen(session, WT_METADATA_TURTLE, 0, WT_STREAM_READ, &fs)); /* Search for the key. */ WT_ERR(__wt_scr_alloc(session, 512, &buf)); for (match = false;;) { WT_ERR(__wt_getline(session, fs, buf)); if (buf->size == 0) WT_ERR(WT_NOTFOUND); if (strcmp(key, buf->data) == 0) match = true; /* Key matched: read the subsequent line for the value. */ WT_ERR(__wt_getline(session, fs, buf)); if (buf->size == 0) WT_ERR(__wt_illegal_value(session, WT_METADATA_TURTLE)); if (match) break; } /* Copy the value for the caller. */ WT_ERR(__wt_strdup(session, buf->data, valuep)); err: WT_TRET(__wt_fclose(session, &fs)); __wt_scr_free(session, &buf); if (ret != 0) __wt_free(session, *valuep); return (ret); }
/* * __metadata_load_bulk -- * Create any bulk-loaded file stubs. */ static int __metadata_load_bulk(WT_SESSION_IMPL *session) { WT_CURSOR *cursor; WT_DECL_RET; uint32_t allocsize; bool exist; const char *filecfg[] = { WT_CONFIG_BASE(session, file_meta), NULL, NULL }; const char *key, *value; /* * If a file was being bulk-loaded during the hot backup, it will appear * in the metadata file, but the file won't exist. Create on demand. */ WT_RET(__wt_metadata_cursor(session, &cursor)); while ((ret = cursor->next(cursor)) == 0) { WT_ERR(cursor->get_key(cursor, &key)); if (!WT_PREFIX_SKIP(key, "file:")) continue; /* If the file exists, it's all good. */ WT_ERR(__wt_fs_exist(session, key, &exist)); if (exist) continue; /* * If the file doesn't exist, assume it's a bulk-loaded file; * retrieve the allocation size and re-create the file. */ WT_ERR(cursor->get_value(cursor, &value)); filecfg[1] = value; WT_ERR(__wt_direct_io_size_check( session, filecfg, "allocation_size", &allocsize)); WT_ERR(__wt_block_manager_create(session, key, allocsize)); } WT_ERR_NOTFOUND_OK(ret); err: WT_TRET(__wt_metadata_cursor_release(session, &cursor)); return (ret); }
/* * __metadata_load_hot_backup -- * Load the contents of any hot backup file. */ static int __metadata_load_hot_backup(WT_SESSION_IMPL *session) { WT_DECL_ITEM(key); WT_DECL_ITEM(value); WT_DECL_RET; WT_FSTREAM *fs; bool exist; /* Look for a hot backup file: if we find it, load it. */ WT_RET(__wt_fs_exist(session, WT_METADATA_BACKUP, &exist)); if (!exist) return (0); WT_RET(__wt_fopen(session, WT_METADATA_BACKUP, 0, WT_STREAM_READ, &fs)); /* Read line pairs and load them into the metadata file. */ WT_ERR(__wt_scr_alloc(session, 512, &key)); WT_ERR(__wt_scr_alloc(session, 512, &value)); for (;;) { WT_ERR(__wt_getline(session, fs, key)); if (key->size == 0) break; WT_ERR(__wt_getline(session, fs, value)); if (value->size == 0) WT_ERR(__wt_illegal_value(session, WT_METADATA_BACKUP)); WT_ERR(__wt_metadata_update(session, key->data, value->data)); } F_SET(S2C(session), WT_CONN_WAS_BACKUP); err: WT_TRET(__wt_fclose(session, &fs)); __wt_scr_free(session, &key); __wt_scr_free(session, &value); 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); }
/* * __wt_turtle_init -- * Check the turtle file and create if necessary. */ int __wt_turtle_init(WT_SESSION_IMPL *session) { WT_DECL_RET; bool exist_backup, exist_incr, exist_isrc, exist_turtle, load; char *metaconf; metaconf = NULL; load = false; /* * Discard any turtle setup file left-over from previous runs. This * doesn't matter for correctness, it's just cleaning up random files. */ WT_RET(__wt_remove_if_exists(session, WT_METADATA_TURTLE_SET, false)); /* * We could die after creating the turtle file and before creating the * metadata file, or worse, the metadata file might be in some random * state. Make sure that doesn't happen: if we don't find the turtle * file, first create the metadata file, load any hot backup, and then * create the turtle file. No matter what happens, if metadata file * creation doesn't fully complete, we won't have a turtle file and we * will repeat the process until we succeed. * * Incremental backups can occur only if recovery is run and it becomes * live. So, if there is a turtle file and an incremental backup file, * that is an error. Otherwise, if there's already a turtle file, we're * done. */ WT_RET(__wt_fs_exist(session, WT_INCREMENTAL_BACKUP, &exist_incr)); WT_RET(__wt_fs_exist(session, WT_INCREMENTAL_SRC, &exist_isrc)); WT_RET(__wt_fs_exist(session, WT_METADATA_BACKUP, &exist_backup)); WT_RET(__wt_fs_exist(session, WT_METADATA_TURTLE, &exist_turtle)); if (exist_turtle) { /* * We need to detect the difference between a source database * that may have crashed with an incremental backup file * and a destination database that incorrectly ran recovery. */ if (exist_incr && !exist_isrc) WT_RET_MSG(session, EINVAL, "Incremental backup after running recovery " "is not allowed"); /* * If we have a backup file and metadata and turtle files, * we want to recreate the metadata from the backup. */ if (exist_backup) { WT_RET(__wt_msg(session, "Both %s and %s exist; recreating metadata from " "backup", WT_METADATA_TURTLE, WT_METADATA_BACKUP)); WT_RET( __wt_remove_if_exists(session, WT_METAFILE, false)); WT_RET(__wt_remove_if_exists( session, WT_METADATA_TURTLE, false)); load = true; } } else load = true; if (load) { if (exist_incr) F_SET(S2C(session), WT_CONN_WAS_BACKUP); /* Create the metadata file. */ WT_RET(__metadata_init(session)); /* Load any hot-backup information. */ WT_RET(__metadata_load_hot_backup(session)); /* Create any bulk-loaded file stubs. */ WT_RET(__metadata_load_bulk(session)); /* Create the turtle file. */ WT_RET(__metadata_config(session, &metaconf)); WT_WITH_TURTLE_LOCK(session, ret, ret = __wt_turtle_update( session, WT_METAFILE_URI, metaconf)); WT_ERR(ret); } /* Remove the backup files, we'll never read them again. */ WT_ERR(__wt_backup_file_remove(session)); err: __wt_free(session, metaconf); return (ret); }