#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "libnvme_impl.h"
bool
nvme_ns_info_error(nvme_ns_info_t *info, nvme_info_err_t err, int32_t sys,
const char *fmt, ...)
{
int ret;
va_list ap;
info->nni_err = err;
info->nni_syserr = sys;
va_start(ap, fmt);
ret = vsnprintf(info->nni_errmsg, sizeof (info->nni_errmsg), fmt, ap);
va_end(ap);
if (ret >= sizeof (info->nni_errmsg)) {
info->nni_errlen = sizeof (info->nni_errmsg) - 1;
} else if (ret <= 0) {
info->nni_errlen = 0;
info->nni_errmsg[0] = '\0';
} else {
info->nni_errlen = (size_t)ret;
}
return (false);
}
bool
nvme_ns_info_success(nvme_ns_info_t *info)
{
info->nni_err = NVME_INFO_ERR_OK;
info->nni_syserr = 0;
info->nni_errmsg[0] = '\0';
info->nni_errlen = 0;
return (true);
}
nvme_info_err_t
nvme_ns_info_err(nvme_ns_info_t *info)
{
return (info->nni_err);
}
int32_t
nvme_ns_info_syserr(nvme_ns_info_t *info)
{
return (info->nni_syserr);
}
const char *
nvme_ns_info_errmsg(nvme_ns_info_t *info)
{
return (info->nni_errmsg);
}
size_t
nvme_ns_info_errlen(nvme_ns_info_t *info)
{
return (info->nni_errlen);
}
const char *
nvme_ns_info_errtostr(nvme_ns_info_t *info, nvme_info_err_t err)
{
return (nvme_ctrl_info_errtostr(NULL, err));
}
void
nvme_ns_info_free(nvme_ns_info_t *info)
{
free(info);
}
bool
nvme_ns_info_snap(nvme_ns_t *ns, nvme_ns_info_t **infop)
{
nvme_ctrl_t *ctrl = ns->nn_ctrl;
nvme_ns_info_t *info;
if (infop == NULL) {
return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
"encountered invalid nvme_ns_info_t output pointer: %p",
infop));
}
info = calloc(1, sizeof (nvme_ns_info_t));
if (info == NULL) {
int e = errno;
return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
"allocate memory for a new nvme_ns_info_t: %s",
strerror(e)));
}
info->nni_nsid = ns->nn_nsid;
if (!nvme_ioc_ns_info(ns->nn_ctrl, ns->nn_nsid, &info->nni_info)) {
nvme_ns_info_free(info);
return (false);
}
info->nni_vers = ns->nn_ctrl->nc_vers;
info->nni_level = nvme_ns_state_to_disc_level(info->nni_info.nni_state);
*infop = info;
return (nvme_ctrl_success(ctrl));
}
bool
nvme_ctrl_ns_info_snap(nvme_ctrl_t *ctrl, uint32_t nsid, nvme_ns_info_t **infop)
{
nvme_ns_info_t *info;
if (infop == NULL) {
return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
"encountered invalid nvme_ns_info_t output pointer: %p",
infop));
}
if (nsid < NVME_NSID_MIN || nsid > ctrl->nc_info.id_nn) {
return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "requested "
"namespace %u is invalid, valid namespaces are [0x%x, "
"0x%x]", nsid, NVME_NSID_MIN, ctrl->nc_info.id_nn));
}
info = calloc(1, sizeof (nvme_ns_info_t));
if (info == NULL) {
int e = errno;
return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
"allocate memory for a new nvme_ns_info_t: %s",
strerror(e)));
}
info->nni_nsid = nsid;
if (!nvme_ioc_ns_info(ctrl, nsid, &info->nni_info)) {
nvme_ns_info_free(info);
return (false);
}
info->nni_vers = ctrl->nc_vers;
info->nni_level = nvme_ns_state_to_disc_level(info->nni_info.nni_state);
*infop = info;
return (nvme_ctrl_success(ctrl));
}
uint32_t
nvme_ns_info_nsid(nvme_ns_info_t *info)
{
return (info->nni_nsid);
}
const nvme_identify_nsid_t *
nvme_ns_info_identify(nvme_ns_info_t *info)
{
return (&info->nni_info.nni_id);
}
nvme_ns_disc_level_t
nvme_ns_info_level(nvme_ns_info_t *info)
{
return (info->nni_level);
}
static bool
nvme_ns_info_req_active(nvme_ns_info_t *info, const nvme_version_t *vers)
{
if (info->nni_level < NVME_NS_DISC_F_ACTIVE) {
return (nvme_ns_info_error(info, NVME_INFO_ERR_NS_INACTIVE, 0,
"information cannot be provided for inactive namespaces: "
"namespace is %s (0x%x)",
nvme_nsleveltostr(info->nni_level), info->nni_level));
}
if (!nvme_vers_ns_info_atleast(info, vers)) {
return (nvme_ns_info_error(info, NVME_INFO_ERR_VERSION, 0,
"cannot provide information, device must be at least "
"version %u.%u, but is %u.%u", vers->v_major, vers->v_minor,
info->nni_vers.v_major, info->nni_vers.v_minor));
}
return (true);
}
bool
nvme_ns_info_nguid(nvme_ns_info_t *info, uint8_t nguid[16])
{
const uint8_t zero_guid[16] = { 0 };
if (!nvme_ns_info_req_active(info, &nvme_vers_1v2)) {
return (false);
}
if (memcmp(zero_guid, info->nni_info.nni_id.id_nguid,
sizeof (zero_guid)) == 0) {
return (nvme_ns_info_error(info, NVME_INFO_ERR_MISSING_CAP, 0,
"Namespace GUID invalid: found all 0s"));
}
(void) memcpy(nguid, info->nni_info.nni_id.id_nguid,
sizeof (info->nni_info.nni_id.id_nguid));
return (nvme_ns_info_success(info));
}
bool
nvme_ns_info_eui64(nvme_ns_info_t *info, uint8_t eui64[8])
{
const uint8_t zero_eui64[8] = { 0 };
if (!nvme_ns_info_req_active(info, &nvme_vers_1v1)) {
return (false);
}
if (memcmp(zero_eui64, info->nni_info.nni_id.id_eui64,
sizeof (zero_eui64)) == 0) {
return (nvme_ns_info_error(info, NVME_INFO_ERR_MISSING_CAP, 0,
"Namespace EUI64 invalid: found all 0s"));
}
(void) memcpy(eui64, info->nni_info.nni_id.id_eui64,
sizeof (info->nni_info.nni_id.id_eui64));
return (nvme_ns_info_success(info));
}
bool
nvme_ns_info_size(nvme_ns_info_t *info, uint64_t *sizep)
{
if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) {
return (false);
}
*sizep = info->nni_info.nni_id.id_nsize;
return (nvme_ns_info_success(info));
}
bool
nvme_ns_info_cap(nvme_ns_info_t *info, uint64_t *capp)
{
if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) {
return (false);
}
*capp = info->nni_info.nni_id.id_ncap;
return (nvme_ns_info_success(info));
}
bool
nvme_ns_info_use(nvme_ns_info_t *info, uint64_t *usep)
{
if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) {
return (false);
}
*usep = info->nni_info.nni_id.id_nuse;
return (nvme_ns_info_success(info));
}
bool
nvme_ns_info_nformats(nvme_ns_info_t *info, uint32_t *nfmtp)
{
if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) {
return (false);
}
*nfmtp = info->nni_info.nni_id.id_nlbaf + 1;
return (nvme_ns_info_success(info));
}
bool
nvme_ns_info_format(nvme_ns_info_t *info, uint32_t idx,
const nvme_nvm_lba_fmt_t **fmtp)
{
uint32_t max;
const nvme_identify_nsid_t *nsid = &info->nni_info.nni_id;
if (!nvme_ns_info_nformats(info, &max)) {
return (false);
}
if (idx >= max) {
return (nvme_ns_info_error(info, NVME_INFO_ERR_BAD_FMT, 0,
"requested index %u is invalid: valid range is [0, %u]",
idx, max - 1));
}
if (!info->nni_lbaf_valid[idx]) {
uint8_t lbads = nsid->id_lbaf[idx].lbaf_lbads;
if (lbads == 0) {
return (nvme_ns_info_error(info, NVME_INFO_ERR_BAD_FMT,
0, "format %u is not actually valid due to 0 LBA "
"data size even though it is considered a valid "
"LBA format by NLBAF", lbads));
}
if (lbads < 9) {
return (nvme_ns_info_error(info,
NVME_INFO_ERR_BAD_FMT_DATA, 0, "NVMe devices are "
"not allowed to have a LBA data size of less than "
"512 bytes, found raw shift value of %u for "
"format %u", lbads, idx));
}
if (lbads >= 64) {
return (nvme_ns_info_error(info,
NVME_INFO_ERR_BAD_FMT_DATA, 0, "LBA format %u has "
"LBA data size greater " "than 64 (%u), cannot be "
"represented as a byte size", idx, lbads));
}
info->nni_lbaf[idx].nnlf_id = idx;
info->nni_lbaf[idx].nnlf_ms = nsid->id_lbaf[idx].lbaf_ms;
info->nni_lbaf[idx].nnlf_lbasz = 1ULL << lbads;
info->nni_lbaf[idx].nnlf_rel = nsid->id_lbaf[idx].lbaf_rp;
info->nni_lbaf_valid[idx] = true;
}
*fmtp = &info->nni_lbaf[idx];
return (nvme_ns_info_success(info));
}
bool
nvme_ns_info_curformat(nvme_ns_info_t *info, const nvme_nvm_lba_fmt_t **fmtp)
{
uint32_t idx;
if (!nvme_ns_info_req_active(info, &nvme_vers_1v0)) {
return (false);
}
idx = info->nni_info.nni_id.id_flbas.lba_format;
return (nvme_ns_info_format(info, idx, fmtp));
}
bool
nvme_ns_info_bd_addr(nvme_ns_info_t *info, const char **addrp)
{
if (info->nni_level < NVME_NS_DISC_F_BLKDEV) {
return (nvme_ns_info_error(info, NVME_INFO_ERR_NS_NO_BLKDEV, 0,
"the blkdev address cannot be provided for namespaces "
"without blkdev attached: namespace is %s (0x%x)",
nvme_nsleveltostr(info->nni_level), info->nni_level));
}
*addrp = info->nni_info.nni_addr;
return (nvme_ns_info_success(info));
}