/* * status_push -- (internal) push single status object */ static int status_push(PMEMpoolcheck *ppc, struct check_status *st, uint32_t question) { if (st->status.type == PMEMPOOL_CHECK_MSG_TYPE_ERROR) { ASSERTeq(ppc->data->error, NULL); ppc->data->error = st; return -1; } else if (st->status.type == PMEMPOOL_CHECK_MSG_TYPE_INFO) { if (CHECK_IS(ppc, VERBOSE)) TAILQ_INSERT_TAIL(&ppc->data->infos, st, next); else check_status_release(ppc, st); return 0; } /* st->status.type == PMEMPOOL_CHECK_MSG_TYPE_QUESTION */ if (CHECK_IS_NOT(ppc, REPAIR)) { /* error status */ if (status_msg_info_only(st->msg)) { ERR("no error message for the user"); st->msg[0] = '\0'; } st->status.type = PMEMPOOL_CHECK_MSG_TYPE_ERROR; return status_push(ppc, st, question); } if (CHECK_IS(ppc, ALWAYS_YES)) { if (!status_msg_info_only(st->msg)) { /* information status */ st->status.type = PMEMPOOL_CHECK_MSG_TYPE_INFO; status_push(ppc, st, question); st = status_alloc(); } /* answer status */ ppc->result = CHECK_RESULT_PROCESS_ANSWERS; st->question = question; st->answer = PMEMPOOL_CHECK_ANSWER_YES; st->status.type = PMEMPOOL_CHECK_MSG_TYPE_QUESTION; TAILQ_INSERT_TAIL(&ppc->data->answers, st, next); } else { /* question message */ status_msg_info_and_question(st->msg); st->question = question; ppc->result = CHECK_RESULT_ASK_QUESTIONS; st->answer = PMEMPOOL_CHECK_ANSWER_EMPTY; TAILQ_INSERT_TAIL(&ppc->data->questions, st, next); } return 0; }
/* * check_status_create -- create single status, push it to proper queue * * MSG_SEPARATOR character in fmt is treated as message separator. If creating * question but check arguments do not allow to make any changes (asking any * question is pointless) it takes part of message before MSG_SEPARATOR * character and use it to create error message. Character just before separator * must be a MSG_PLACE_OF_SEPARATION character. Return non 0 value if error * status would be created. */ int check_status_create(PMEMpoolcheck *ppc, enum pmempool_check_msg_type type, uint32_t question, const char *fmt, ...) { if (CHECK_IS_NOT(ppc, VERBOSE) && type == PMEMPOOL_CHECK_MSG_TYPE_INFO) return 0; struct check_status *st = status_alloc(); if (CHECK_IS(ppc, FORMAT_STR)) { va_list ap; va_start(ap, fmt); int p = vsnprintf(st->msg, MAX_MSG_STR_SIZE, fmt, ap); va_end(ap); /* append possible strerror at the end of the message */ if (type != PMEMPOOL_CHECK_MSG_TYPE_QUESTION && errno && p > 0) { char buff[UTIL_MAX_ERR_MSG]; util_strerror(errno, buff, UTIL_MAX_ERR_MSG); snprintf(st->msg + p, MAX_MSG_STR_SIZE - (size_t)p, ": %s", buff); } st->status.type = type; } return status_push(ppc, st, question); }
/* * pool_hdr_preliminary_check -- (internal) check pool header checksum and pool * parameters */ static int pool_hdr_preliminary_check(PMEMpoolcheck *ppc, location *loc) { LOG(3, NULL); CHECK_INFO(ppc, "%schecking pool header", loc->prefix); if (util_is_zeroed((void *)&loc->hdr, sizeof(loc->hdr))) { if (CHECK_IS_NOT(ppc, REPAIR)) { check_end(ppc->data); ppc->result = CHECK_RESULT_NOT_CONSISTENT; return CHECK_ERR(ppc, "%sempty pool hdr", loc->prefix); } } else if (loc->hdr_valid) { enum pool_type type = pool_hdr_get_type(&loc->hdr); if (type == POOL_TYPE_UNKNOWN) { if (CHECK_IS_NOT(ppc, REPAIR)) { check_end(ppc->data); ppc->result = CHECK_RESULT_NOT_CONSISTENT; return CHECK_ERR(ppc, "%sinvalid signature", loc->prefix); } CHECK_INFO(ppc, "%sinvalid signature", loc->prefix); } else { /* valid check sum */ CHECK_INFO(ppc, "%spool header correct", loc->prefix); loc->step = CHECK_STEP_COMPLETE; return 0; } } else if (CHECK_IS_NOT(ppc, REPAIR)) { check_end(ppc->data); ppc->result = CHECK_RESULT_NOT_CONSISTENT; return CHECK_ERR(ppc, "%sincorrect pool header", loc->prefix); } else { CHECK_INFO(ppc, "%sincorrect pool header", loc->prefix); } ASSERT(CHECK_IS(ppc, REPAIR)); if (ppc->pool->params.type == POOL_TYPE_UNKNOWN) { ppc->pool->params.type = pool_hdr_possible_type(ppc); if (ppc->pool->params.type == POOL_TYPE_UNKNOWN) { ppc->result = CHECK_RESULT_CANNOT_REPAIR; return CHECK_ERR(ppc, "cannot determine pool type"); } } if (!pool_supported(ppc->pool->params.type)) { ppc->result = CHECK_RESULT_CANNOT_REPAIR; return CHECK_ERR(ppc, "the repair of %s pools is not supported", pool_get_pool_type_str(ppc->pool->params.type)); } return 0; }
/* * pool_data_alloc -- allocate pool data and open set_file */ struct pool_data * pool_data_alloc(PMEMpoolcheck *ppc) { LOG(3, NULL); struct pool_data *pool = malloc(sizeof(*pool)); if (!pool) { ERR("!malloc"); return NULL; } TAILQ_INIT(&pool->arenas); pool->narenas = 0; pool->blk_no_layout = 0; pool->uuid_op = UUID_NOP; pool->set_file = NULL; pool->bttc.valid = false; if (pool_params_parse(ppc, &pool->params, 0)) goto error; int rdonly = CHECK_IS_NOT(ppc, REPAIR); int prv = CHECK_IS(ppc, DRY_RUN); if (prv && pool->params.is_device_dax) { errno = ENOTSUP; ERR("!cannot perform a dry run on dax device"); goto error; } pool->set_file = pool_set_file_open(ppc->path, &pool->params, prv); if (pool->set_file == NULL) goto error; /* * XXX mprotect for device dax with length not aligned to its * page granularity causes SIGBUS on the next page fault. * The length argument of this call should be changed to * pool->set_file->poolsize once the kernel issue is solved. */ if (rdonly && mprotect(pool->set_file->addr, pool->set_file->poolset->replica[0]->repsize, PROT_READ) < 0) goto error; if (pool->params.type != POOL_TYPE_BTT) { if (pool_set_file_map_headers(pool->set_file, rdonly, prv)) goto error; } return pool; error: pool_data_free(pool); return NULL; }
/* * pool_hdr_default_check -- (internal) check some default values in pool header */ static int pool_hdr_default_check(PMEMpoolcheck *ppc, location *loc) { LOG(3, NULL); ASSERT(CHECK_IS(ppc, REPAIR)); struct pool_hdr def_hdr; pool_hdr_default(ppc->pool->params.type, &def_hdr); if (memcmp(loc->hdr.signature, def_hdr.signature, POOL_HDR_SIG_LEN)) { CHECK_ASK(ppc, Q_DEFAULT_SIGNATURE, "%spool_hdr.signature is not valid.|Do you want to set " "it to %.8s?", loc->prefix, def_hdr.signature); } if (loc->hdr.major != def_hdr.major) { CHECK_ASK(ppc, Q_DEFAULT_MAJOR, "%spool_hdr.major is not valid.|Do you want to set it " "to default value 0x%x?", loc->prefix, def_hdr.major); } if (loc->hdr.compat_features != def_hdr.compat_features) { CHECK_ASK(ppc, Q_DEFAULT_COMPAT_FEATURES, "%spool_hdr.compat_features is not valid.|Do you want " "to set it to default value 0x%x?", loc->prefix, def_hdr.compat_features); } if (loc->hdr.incompat_features != def_hdr.incompat_features) { CHECK_ASK(ppc, Q_DEFAULT_INCOMPAT_FEATURES, "%spool_hdr.incompat_features is not valid.|Do you " "want to set it to default value 0x%x?", loc->prefix, def_hdr.incompat_features); } if (loc->hdr.ro_compat_features != def_hdr.ro_compat_features) { CHECK_ASK(ppc, Q_DEFAULT_RO_COMPAT_FEATURES, "%spool_hdr.ro_compat_features is not valid.|Do you " "want to set it to default value 0x%x?", loc->prefix, def_hdr.ro_compat_features); } if (!util_is_zeroed(loc->hdr.unused, sizeof(loc->hdr.unused))) { CHECK_ASK(ppc, Q_ZERO_UNUSED_AREA, "%sunused area is not filled by zeros.|Do you want to " "fill it up?", loc->prefix); } return check_questions_sequence_validate(ppc); }
/* * pool_hdr_uuid_links -- (internal) check UUID links values */ static int pool_hdr_uuid_links(PMEMpoolcheck *ppc, location *loc) { LOG(3, NULL); /* * If the pool header is valid and there is not other parts or replicas * in the poolset its uuid links are also valid. */ if (loc->hdr_valid && loc->single_repl && loc->single_part) return 0; uuid_t *links[] = { &loc->hdr.next_part_uuid, &loc->hdr.prev_part_uuid, &loc->hdr.next_repl_uuid, &loc->hdr.prev_repl_uuid}; uuid_t *uuids[] = { &loc->next_part_hdrp->uuid, &loc->prev_part_hdrp->uuid, &loc->next_repl_hdrp->uuid, &loc->prev_repl_hdrp->uuid }; uint32_t questions[] = { Q_NEXT_PART_UUID_SET, Q_PREV_PART_UUID_SET, Q_NEXT_REPL_UUID_SET, Q_PREV_REPL_UUID_SET }; const char *fields[] = { "pool_hdr.next_part_uuid", "pool_hdr.prev_part_uuid", "pool_hdr.next_repl_uuid", "pool_hdr.prev_repl_uuid" }; COMPILE_ERROR_ON(ARRAY_SIZE(links) != ARRAY_SIZE(uuids)); COMPILE_ERROR_ON(ARRAY_SIZE(links) != ARRAY_SIZE(questions)); COMPILE_ERROR_ON(ARRAY_SIZE(links) != ARRAY_SIZE(fields)); for (uint64_t i = 0; i < ARRAY_SIZE(links); ++i) { if (uuidcmp(*links[i], *uuids[i]) == 0) continue; if (CHECK_IS(ppc, REPAIR)) { CHECK_ASK(ppc, questions[i], "%sinvalid %s.|Do you want to set it to a " "valid value?", loc->prefix, fields[i]); } else { ppc->result = CHECK_RESULT_NOT_CONSISTENT; return CHECK_ERR(ppc, "%sinvalid %s", loc->prefix, fields[i]); } } return check_questions_sequence_validate(ppc); }
/* * pool_hdr_uuid_find -- (internal) check UUID value */ static int pool_hdr_uuid_find(PMEMpoolcheck *ppc, location *loc) { LOG(3, NULL); /* * If the pool header is valid and there is not other parts or replicas * in the poolset its uuid is also valid. */ if (loc->hdr_valid && loc->single_repl && loc->single_part) return 0; int hdrs_valid[] = { loc->next_part_hdr_valid, loc->prev_part_hdr_valid, loc->next_repl_hdr_valid, loc->prev_repl_hdr_valid}; uuid_t *uuids[] = { &loc->next_part_hdrp->prev_part_uuid, &loc->prev_part_hdrp->next_part_uuid, &loc->next_repl_hdrp->prev_repl_uuid, &loc->prev_repl_hdrp->next_repl_uuid }; /* * if all valid poolset part files have the same uuid links to this part * file it is valid uuid * if all links have the same uuid and it is single file pool it is also * the valid uuid */ loc->valid_uuid = NULL; if (loc->hdr_valid) loc->valid_uuid = &loc->hdr.uuid; uuid_t *common_uuid = uuids[0]; COMPILE_ERROR_ON(ARRAY_SIZE(uuids) != ARRAY_SIZE(hdrs_valid)); COMPILE_ERROR_ON(COMPARE_TO_FIRST_PART_ONLY >= ARRAY_SIZE(uuids)); for (unsigned i = 0; i < ARRAY_SIZE(uuids); ++i) { if (i > 0 && common_uuid != NULL) { if (uuidcmp(*common_uuid, *uuids[i]) != 0) { common_uuid = NULL; } } if (i >= COMPARE_TO_FIRST_PART_ONLY && loc->part != 0) continue; if (!hdrs_valid[i]) continue; if (!loc->valid_uuid) { loc->valid_uuid = uuids[i]; } else if (uuidcmp(*loc->valid_uuid, *uuids[i]) != 0) { ppc->result = CHECK_RESULT_NOT_CONSISTENT; return CHECK_ERR(ppc, "%sambiguous pool_hdr.uuid", loc->prefix); } } if (!loc->valid_uuid && common_uuid) loc->valid_uuid = common_uuid; if (loc->valid_uuid != NULL) { if (uuidcmp(*loc->valid_uuid, loc->hdr.uuid) != 0) { CHECK_ASK(ppc, Q_UUID_SET, INVALID_UUID ".|Do you want " "to set it to %s from a valid part file?", loc->prefix, check_get_uuid_str(*loc->valid_uuid)); } } else if (CHECK_IS(ppc, ADVANCED)) { CHECK_ASK(ppc, Q_UUID_REGENERATE, INVALID_UUID ".|Do you want " "to regenerate it?", loc->prefix); } else if (CHECK_IS(ppc, REPAIR)) { ppc->result = CHECK_RESULT_CANNOT_REPAIR; CHECK_INFO(ppc, "%s" REQUIRE_ADVANCED, loc->prefix); return CHECK_ERR(ppc, INVALID_UUID, loc->prefix); } else { ppc->result = CHECK_RESULT_NOT_CONSISTENT; return CHECK_ERR(ppc, INVALID_UUID, loc->prefix); } return check_questions_sequence_validate(ppc); }