/* * __wt_meta_ckptlist_get -- * Load all available checkpoint information for a file. */ int __wt_meta_ckptlist_get( WT_SESSION_IMPL *session, const char *fname, WT_CKPT **ckptbasep) { WT_CKPT *ckpt, *ckptbase; WT_CONFIG ckptconf; WT_CONFIG_ITEM k, v; WT_DECL_ITEM(buf); WT_DECL_RET; size_t allocated, slot; char *config; *ckptbasep = NULL; ckptbase = NULL; allocated = slot = 0; config = NULL; /* Retrieve the metadata information for the file. */ WT_RET(__wt_metadata_search(session, fname, &config)); /* Load any existing checkpoints into the array. */ WT_ERR(__wt_scr_alloc(session, 0, &buf)); if (__wt_config_getones(session, config, "checkpoint", &v) == 0) { __wt_config_subinit(session, &ckptconf, &v); for (; __wt_config_next(&ckptconf, &k, &v) == 0; ++slot) { WT_ERR(__wt_realloc_def( session, &allocated, slot + 1, &ckptbase)); ckpt = &ckptbase[slot]; WT_ERR(__ckpt_load(session, &k, &v, ckpt)); } } /* * Allocate an extra slot for a new value, plus a slot to mark the end. * * This isn't very clean, but there's necessary cooperation between the * schema layer (that maintains the list of checkpoints), the btree * layer (that knows when the root page is written, creating a new * checkpoint), and the block manager (which actually creates the * checkpoint). All of that cooperation is handled in the WT_CKPT * structure referenced from the WT_BTREE structure. */ WT_ERR(__wt_realloc_def(session, &allocated, slot + 2, &ckptbase)); /* Sort in creation-order. */ qsort(ckptbase, slot, sizeof(WT_CKPT), __ckpt_compare_order); /* Return the array to our caller. */ *ckptbasep = ckptbase; if (0) { err: __wt_meta_ckptlist_free(session, &ckptbase); } __wt_free(session, config); __wt_scr_free(session, &buf); return (ret); }
/* * __wt_salvage -- * Salvage a single file. */ int __wt_salvage(WT_SESSION_IMPL *session, const char *cfg[]) { WT_CKPT *ckptbase; WT_DATA_HANDLE *dhandle; WT_DECL_RET; dhandle = session->dhandle; /* * XXX * The salvage process reads and discards previous checkpoints, so the * underlying block manager has to ignore any previous checkpoint * entries when creating a new checkpoint, in other words, we can't use * the metadata checkpoint list, it has all of those checkpoint listed * and we don't care about them. Build a clean checkpoint list and use * it instead. * * Don't first clear the metadata checkpoint list and call the function * to get a list of checkpoints: a crash between clearing the metadata * checkpoint list and creating a new checkpoint list would look like a * create or open of a file without a checkpoint to roll-forward from, * and the contents of the file would be discarded. */ WT_RET(__wt_calloc_def(session, 2, &ckptbase)); WT_ERR(__wt_strdup(session, WT_CHECKPOINT, &ckptbase[0].name)); F_SET(&ckptbase[0], WT_CKPT_ADD); WT_ERR(__wt_bt_salvage(session, ckptbase, cfg)); /* * If no checkpoint was created, well, it's probably bad news, but there * is nothing to do but clear any recorded checkpoints for the file. If * a checkpoint was created, life is good, replace any existing list of * checkpoints with the single new one. */ if (ckptbase[0].raw.data == NULL) WT_ERR(__wt_meta_checkpoint_clear(session, dhandle->name)); else WT_ERR(__wt_meta_ckptlist_set( session, dhandle->name, ckptbase, NULL)); err: __wt_meta_ckptlist_free(session, &ckptbase); return (ret); }
/* * __wt_meta_ckptlist_get -- * Load all available checkpoint information for a file. */ int __wt_meta_ckptlist_get( WT_SESSION_IMPL *session, const char *fname, WT_CKPT **ckptbasep) { WT_CKPT *ckpt, *ckptbase; WT_CONFIG ckptconf; WT_CONFIG_ITEM a, k, v; WT_DECL_RET; WT_ITEM *buf; size_t allocated, slot; const char *config; char timebuf[64]; *ckptbasep = NULL; buf = NULL; ckptbase = NULL; allocated = slot = 0; config = NULL; /* Retrieve the metadata information for the file. */ WT_RET(__wt_metadata_read(session, fname, &config)); /* Load any existing checkpoints into the array. */ WT_ERR(__wt_scr_alloc(session, 0, &buf)); if (__wt_config_getones(session, config, "checkpoint", &v) == 0 && __wt_config_subinit(session, &ckptconf, &v) == 0) for (; __wt_config_next(&ckptconf, &k, &v) == 0; ++slot) { if (slot * sizeof(WT_CKPT) == allocated) WT_ERR(__wt_realloc(session, &allocated, (slot + 50) * sizeof(WT_CKPT), &ckptbase)); ckpt = &ckptbase[slot]; /* * Copy the name, address (raw and hex), order and time * into the slot. If there's no address, it's a fake. */ WT_ERR( __wt_strndup(session, k.str, k.len, &ckpt->name)); WT_ERR(__wt_config_subgets(session, &v, "addr", &a)); WT_ERR( __wt_buf_set(session, &ckpt->addr, a.str, a.len)); if (a.len == 0) F_SET(ckpt, WT_CKPT_FAKE); else WT_ERR(__wt_nhex_to_raw( session, a.str, a.len, &ckpt->raw)); WT_ERR(__wt_config_subgets(session, &v, "order", &a)); if (a.val == 0) goto format; ckpt->order = a.val; WT_ERR(__wt_config_subgets(session, &v, "time", &a)); if (a.len == 0) goto format; if (a.len > sizeof(timebuf) - 1) goto format; memcpy(timebuf, a.str, a.len); timebuf[a.len] = '\0'; if (sscanf(timebuf, "%" SCNuMAX, &ckpt->sec) != 1) goto format; WT_ERR(__wt_config_subgets(session, &v, "size", &a)); ckpt->ckpt_size = (uint64_t)a.val; } /* * Allocate an extra slot for a new value, plus a slot to mark the end. * * This isn't very clean, but there's necessary cooperation between the * schema layer (that maintains the list of checkpoints), the btree * layer (that knows when the root page is written, creating a new * checkpoint), and the block manager (which actually creates the * checkpoint). All of that cooperation is handled in the WT_CKPT * structure referenced from the WT_BTREE structure. */ if ((slot + 2) * sizeof(WT_CKPT) > allocated) WT_ERR(__wt_realloc(session, &allocated, (slot + 2) * sizeof(WT_CKPT), &ckptbase)); /* Sort in creation-order. */ qsort(ckptbase, slot, sizeof(WT_CKPT), __ckpt_compare_order); /* Return the array to our caller. */ *ckptbasep = ckptbase; if (0) { format: WT_ERR_MSG(session, WT_ERROR, "corrupted checkpoint list"); err: __wt_meta_ckptlist_free(session, ckptbase); } __wt_free(session, config); __wt_scr_free(&buf); return (ret); }