// SPDX-License-Identifier: LGPL-2.1-or-later
/*
 * This file is part of libnvme.
 * Copyright (c) 2020 Western Digital Corporation or its affiliates.
 *
 * Authors: Keith Busch <keith.busch@wdc.com>
 * 	    Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
 */

#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifdef CONFIG_FABRICS
#include <ifaddrs.h>

#include <shared/net-util.h>
#endif

#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>

#if NVME_HAVE_BCRYPT
#include <windows.h>
#include <bcrypt.h>
#endif

#include <ccan/endian/endian.h>

#include <shared/compiler-attributes-util.h>
#include <shared/fs-util.h>

#include <libnvme.h>

#include "cleanup.h"
#include "cleanup-linux.h"
#include "private.h"
#include "util.h"

/* The bionic libc implementation doesn't define LINE_MAX */
#ifndef LINE_MAX
#define LINE_MAX 2048
#endif

/* Some error codes aren't defined on all platforms. Use best equivalents. */
#ifndef EREMOTEIO
#define EREMOTEIO ENXIO
#endif
#ifndef EDQUOT
#define EDQUOT    ENOSPC
#endif
#ifndef ERESTART
#define ERESTART  EAGAIN
#endif

/* Source Code Control System, query version of binary with 'what' */
const char sccsid[] = "@(#)libnvme " GIT_VERSION;

static inline __u8 nvme_generic_status_to_errno(__u16 status)
{
	switch (status) {
	case NVME_SC_INVALID_OPCODE:
	case NVME_SC_INVALID_FIELD:
	case NVME_SC_INVALID_NS:
	case NVME_SC_SGL_INVALID_LAST:
	case NVME_SC_SGL_INVALID_COUNT:
	case NVME_SC_SGL_INVALID_DATA:
	case NVME_SC_SGL_INVALID_METADATA:
	case NVME_SC_SGL_INVALID_TYPE:
	case NVME_SC_SGL_INVALID_OFFSET:
	case NVME_SC_PRP_INVALID_OFFSET:
	case NVME_SC_CMB_INVALID_USE:
	case NVME_SC_KAT_INVALID:
	case NVME_SC_INVALID_KEY_TAG:
	case NVME_SC_INCORRECT_KEY:
	case NVME_SC_INVALID_VALUE_SIZE:
	case NVME_SC_INVALID_KEY_SIZE:
		return EINVAL;
	case NVME_SC_CMDID_CONFLICT:
		return EADDRINUSE;
	case NVME_SC_DATA_XFER_ERROR:
	case NVME_SC_INTERNAL:
	case NVME_SC_SANITIZE_FAILED:
		return EIO;
	case NVME_SC_POWER_LOSS:
	case NVME_SC_ABORT_REQ:
	case NVME_SC_ABORT_QUEUE:
	case NVME_SC_FUSED_FAIL:
	case NVME_SC_FUSED_MISSING:
		return EWOULDBLOCK;
	case NVME_SC_CMD_SEQ_ERROR:
		return EILSEQ;
	case NVME_SC_SANITIZE_IN_PROGRESS:
	case NVME_SC_FORMAT_IN_PROGRESS:
		return EINPROGRESS;
	case NVME_SC_NS_WRITE_PROTECTED:
	case NVME_SC_NS_NOT_READY:
	case NVME_SC_RESERVATION_CONFLICT:
	case NVME_SC_OP_DENIED:
	case NVME_SC_ADMIN_CMD_MEDIA_NOT_READY:
		return EACCES;
	case NVME_SC_LBA_RANGE:
		return EREMOTEIO;
	case NVME_SC_CAP_EXCEEDED:
	case NVME_SC_AWU_EXCEEDED:
		return ENOSPC;
	}
	return EIO;
}

static inline __u8 nvme_cmd_specific_status_to_errno(__u16 status)
{
	switch (status) {
	case NVME_SC_CQ_INVALID:
	case NVME_SC_QID_INVALID:
	case NVME_SC_QUEUE_SIZE:
	case NVME_SC_FIRMWARE_SLOT:
	case NVME_SC_FIRMWARE_IMAGE:
	case NVME_SC_INVALID_VECTOR:
	case NVME_SC_INVALID_LOG_PAGE:
	case NVME_SC_INVALID_FORMAT:
	case NVME_SC_INVALID_QUEUE:
	case NVME_SC_NS_INSUFFICIENT_CAP:
	case NVME_SC_NS_ID_UNAVAILABLE:
	case NVME_SC_CTRL_LIST_INVALID:
	case NVME_SC_BAD_ATTRIBUTES:
	case NVME_SC_INVALID_PI:
	case NVME_SC_INVALID_CTRL_ID:
	case NVME_SC_INVALID_SEC_CTRL_STATE:
	case NVME_SC_INVALID_CTRL_RESOURCES:
	case NVME_SC_INVALID_RESOURCE_ID:
	case NVME_SC_ANA_GROUP_ID_INVALID:
	case NVME_SC_INSUFFICIENT_CAP:
	case NVME_SC_INVALID_IOCS:
	case NVME_SC_ID_UNAVAILABLE:
		return EINVAL;
	case NVME_SC_ABORT_LIMIT:
	case NVME_SC_ASYNC_LIMIT:
	case NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED:
		return EDQUOT;
	case NVME_SC_FW_NEEDS_CONV_RESET:
	case NVME_SC_FW_NEEDS_SUBSYS_RESET:
	case NVME_SC_FW_NEEDS_MAX_TIME:
	case NVME_SC_FW_NEEDS_RESET:
		return ERESTART;
	case NVME_SC_FEATURE_NOT_SAVEABLE:
	case NVME_SC_FEATURE_NOT_CHANGEABLE:
	case NVME_SC_FEATURE_NOT_PER_NS:
	case NVME_SC_FW_ACTIVATE_PROHIBITED:
	case NVME_SC_NS_IS_PRIVATE:
	case NVME_SC_BP_WRITE_PROHIBITED:
	case NVME_SC_READ_ONLY:
	case NVME_SC_PMR_SAN_PROHIBITED:
		return EPERM;
	case NVME_SC_OVERLAPPING_RANGE:
	case NVME_SC_NS_NOT_ATTACHED:
		return ENOSPC;
	case NVME_SC_NS_ALREADY_ATTACHED:
		return EALREADY;
	case NVME_SC_THIN_PROV_NOT_SUPP:
	case NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED:
		return EOPNOTSUPP;
	case NVME_SC_ABORT_MISSING:
		return EWOULDBLOCK;
	case NVME_SC_SELF_TEST_IN_PROGRESS:
		return EINPROGRESS;
	}

	return EIO;
}

static inline __u8 nvme_fabrics_status_to_errno(__u16 status)
{
	switch (status) {
	case NVME_SC_CONNECT_FORMAT:
	case NVME_SC_CONNECT_INVALID_PARAM:
	case NVME_SC_DISCONNECT_INVALID_QTYPE:
		return EINVAL;
	case NVME_SC_CONNECT_CTRL_BUSY:
		return EBUSY;
	case NVME_SC_CONNECT_RESTART_DISC:
		return ERESTART;
	case NVME_SC_CONNECT_INVALID_HOST:
		return ECONNREFUSED;
	case NVME_SC_DISCOVERY_RESTART:
		return EAGAIN;
	case NVME_SC_AUTH_REQUIRED:
		return EPERM;
	}

	return EIO;
}

__shr_public __u8 libnvme_status_to_errno(int status, bool fabrics)
{
	__u16 sc;

	if (!status)
		return 0;
	if (status < 0)
		return -status;

	sc = nvme_status_code(status);
	switch (nvme_status_code_type(status)) {
	case NVME_SCT_GENERIC:
		return nvme_generic_status_to_errno(sc);
	case NVME_SCT_CMD_SPECIFIC:
		if  (fabrics)
			return nvme_fabrics_status_to_errno(sc);
		return nvme_cmd_specific_status_to_errno(sc);
	default:
		return EIO;
	}
}

static const char * const generic_status[] = {
	[NVME_SC_SUCCESS]			  = "Successful Completion: The command completed without error",
	[NVME_SC_INVALID_OPCODE]		  = "Invalid Command Opcode: A reserved coded value or an unsupported value in the command opcode field",
	[NVME_SC_INVALID_FIELD]			  = "Invalid Field in Command: A reserved coded value or an unsupported value in a defined field",
	[NVME_SC_CMDID_CONFLICT]		  = "Command ID Conflict: The command identifier is already in use",
	[NVME_SC_DATA_XFER_ERROR]		  = "Data Transfer Error: Transferring the data or metadata associated with a command experienced an error",
	[NVME_SC_POWER_LOSS]			  = "Commands Aborted due to Power Loss Notification: Indicates that the command was aborted due to a power loss notification",
	[NVME_SC_INTERNAL]			  = "Internal Error: The command was not completed successfully due to an internal error",
	[NVME_SC_ABORT_REQ]			  = "Command Abort Requested: The command was aborted due to an Abort command",
	[NVME_SC_ABORT_QUEUE]			  = "Command Aborted due to SQ Deletion: The command was aborted due to a Delete I/O Submission Queue",
	[NVME_SC_FUSED_FAIL]			  = "Command Aborted due to Failed Fused Command: The command was aborted due to the other command in a fused operation failing",
	[NVME_SC_FUSED_MISSING]			  = "Command Aborted due to Missing Fused Command: The fused command was aborted due to the adjacent submission queue entry not containing a fused command",
	[NVME_SC_INVALID_NS]			  = "Invalid Namespace or Format: The namespace or the format of that namespace is invalid",
	[NVME_SC_CMD_SEQ_ERROR]			  = "Command Sequence Error: The command was aborted due to a protocol violation in a multi- command sequence",
	[NVME_SC_SGL_INVALID_LAST]		  = "Invalid SGL Segment Descriptor: The command includes an invalid SGL Last Segment or SGL Segment descriptor",
	[NVME_SC_SGL_INVALID_COUNT]		  = "Invalid Number of SGL Descriptors: There is an SGL Last Segment descriptor or an SGL Segment descriptor in a location other than the last descriptor of a segment based on the length indicated",
	[NVME_SC_SGL_INVALID_DATA]		  = "Data SGL Length Invalid: The length of a Data SGL is too short or too long and the controller does not support SGL transfers longer than the amount of data to be transferred",
	[NVME_SC_SGL_INVALID_METADATA]		  = "Metadata SGL Length Invalid: The length of a Metadata SGL is too short or too long and the controller does not support SGL transfers longer than the amount of data to be transferred",
	[NVME_SC_SGL_INVALID_TYPE]		  = "SGL Descriptor Type Invalid: The type of an SGL Descriptor is a type that is not supported by the controller",
	[NVME_SC_CMB_INVALID_USE]		  = "Invalid Use of Controller Memory Buffer: The attempted use of the Controller Memory Buffer is not supported by the controller",
	[NVME_SC_PRP_INVALID_OFFSET]		  = "PRP Offset Invalid: The Offset field for a PRP entry is invalid",
	[NVME_SC_AWU_EXCEEDED]			  = "Atomic Write Unit Exceeded: The length specified exceeds the atomic write unit size",
	[NVME_SC_OP_DENIED]			  = "Operation Denied: The command was denied due to lack of access rights",
	[NVME_SC_SGL_INVALID_OFFSET]		  = "SGL Offset Invalid: The offset specified in a descriptor is invalid",
	[NVME_SC_HOSTID_FORMAT]			  = "Host Identifier Inconsistent Format: The NVM subsystem detected the simultaneous use of 64- bit and 128-bit Host Identifier values on different controllers",
	[NVME_SC_KAT_EXPIRED]			  = "Keep Alive Timer Expired: The Keep Alive Timer expired",
	[NVME_SC_KAT_INVALID]			  = "Keep Alive Timeout Invalid: The Keep Alive Timeout value specified is invalid",
	[NVME_SC_CMD_ABORTED_PREMEPT]		  = "Command Aborted due to Preempt and Abort: The command was aborted due to a Reservation Acquire command",
	[NVME_SC_SANITIZE_FAILED]		  = "Sanitize Failed: The most recent sanitize operation failed and no recovery action has been successfully completed",
	[NVME_SC_SANITIZE_IN_PROGRESS]		  = "Sanitize In Progress: The requested function is prohibited while a sanitize operation is in progress",
	[NVME_SC_SGL_INVALID_GRANULARITY]	  = "SGL Data Block Granularity Invalid: The Address alignment or Length granularity for an SGL Data Block descriptor is invalid",
	[NVME_SC_CMD_IN_CMBQ_NOT_SUPP]		  = "Command Not Supported for Queue in CMB: The controller does not support Submission Queue in the Controller Memory Buffer or Completion Queue in the Controller Memory Buffer",
	[NVME_SC_NS_WRITE_PROTECTED]		  = "Namespace is Write Protected: The command is prohibited while the namespace is write protected",
	[NVME_SC_CMD_INTERRUPTED]		  = "Command Interrupted: Command processing was interrupted and the controller is unable to successfully complete the command",
	[NVME_SC_TRAN_TPORT_ERROR]		  = "Transient Transport Error: A transient transport error was detected",
	[NVME_SC_PROHIBITED_BY_CMD_AND_FEAT]	  = "Command Prohibited by Command and Feature Lockdown: The command was aborted due to command execution being prohibited by the Command and Feature Lockdown",
	[NVME_SC_ADMIN_CMD_MEDIA_NOT_READY]	  = "Admin Command Media Not Ready: The Admin command requires access to media and the media is not ready",
	[NVME_SC_INVALID_KEY_TAG]		  = "The command was aborted due to an invalid KEYTAG field value",
	[NVME_SC_HOST_DISPERSED_NS_NOT_ENABLED]	  = "The command is prohibited while the Host Disperesed Namespace Support (HDISNS) field is not set to 1h in the Host Behavior Support feature",
	[NVME_SC_HOST_ID_NOT_INITIALIZED]	  = "Host Identifier Not Initialized",
	[NVME_SC_INCORRECT_KEY]			  = "The command was aborted due to the key associated with the KEYTAG field being incorrect",
	[NVME_SC_FAILED_TO_RESTORE_CONFIG]	  = "Failed to Restore Configuration: The command was aborted due to the command failing to restore configuration",
	[NVME_SC_LBA_RANGE]			  = "LBA Out of Range: The command references an LBA that exceeds the size of the namespace",
	[NVME_SC_CAP_EXCEEDED]			  = "Capacity Exceeded: Execution of the command has caused the capacity of the namespace to be exceeded",
	[NVME_SC_NS_NOT_READY]			  = "Namespace Not Ready: The namespace is not ready to be accessed",
	[NVME_SC_RESERVATION_CONFLICT]		  = "Reservation Conflict: The command was aborted due to a conflict with a reservation held on the accessed namespace",
	[NVME_SC_FORMAT_IN_PROGRESS]		  = "Format In Progress: A Format NVM command is in progress on the namespace",
	[NVME_SC_INVALID_VALUE_SIZE]		  = "The value size is not valid",
	[NVME_SC_INVALID_KEY_SIZE]		  = "The KV key size is not valid",
	[NVME_SC_KV_KEY_NOT_EXISTS]		  = "The Store If Key Exists (SIKE) bit is set to '1' in the Store Option field and the KV key does not exists",
	[NVME_SC_UNRECOVERED_ERROR]		  = "There was an unrecovered error when reading from the medium",
	[NVME_SC_KEY_EXISTS]			  = "The Store If No Key Exists (SINKE) bit is set to '1' in the Store Option field and the KV key exists",
};

static const char * const cmd_spec_status[] = {
	[NVME_SC_CQ_INVALID]			  = "Completion Queue Invalid: The Completion Queue identifier specified in the command does not exist",
	[NVME_SC_QID_INVALID]			  = "Invalid Queue Identifier: The creation of the I/O Completion Queue failed due to an invalid queue identifier specified as part of the command",
	[NVME_SC_QUEUE_SIZE]			  = "Invalid Queue Size: The host attempted to create an I/O Completion Queue with an invalid number of entries",
	[NVME_SC_ABORT_LIMIT]			  = "Abort Command Limit Exceeded: The number of concurrently outstanding Abort commands has exceeded the limit indicated in the Identify Controller data structure",
	[NVME_SC_ABORT_MISSING]			  = "Abort Command Is Missing: The abort command is missing",
	[NVME_SC_ASYNC_LIMIT]			  = "Asynchronous Event Request Limit Exceeded: The number of concurrently outstanding Asynchronous Event Request commands has been exceeded",
	[NVME_SC_FIRMWARE_SLOT]			  = "Invalid Firmware Slot: The firmware slot indicated is invalid or read only",
	[NVME_SC_FIRMWARE_IMAGE]		  = "Invalid Firmware Image: The firmware image specified for activation is invalid and not loaded by the controller",
	[NVME_SC_INVALID_VECTOR]		  = "Invalid Interrupt Vector: The creation of the I/O Completion Queue failed due to an invalid interrupt vector specified as part of the command",
	[NVME_SC_INVALID_LOG_PAGE]		  = "Invalid Log Page: The log page indicated is invalid",
	[NVME_SC_INVALID_FORMAT]		  = "Invalid Format: The LBA Format specified is not supported",
	[NVME_SC_FW_NEEDS_CONV_RESET]		  = "Firmware Activation Requires Conventional Reset: The firmware commit was successful, however, activation of the firmware image requires a conventional reset",
	[NVME_SC_INVALID_QUEUE]			  = "Invalid Queue Deletion: Invalid I/O Completion Queue specified to delete",
	[NVME_SC_FEATURE_NOT_SAVEABLE]		  = "Feature Identifier Not Saveable: The Feature Identifier specified does not support a saveable value",
	[NVME_SC_FEATURE_NOT_CHANGEABLE]	  = "Feature Not Changeable: The Feature Identifier is not able to be changed",
	[NVME_SC_FEATURE_NOT_PER_NS]		  = "Feature Not Namespace Specific: The Feature Identifier specified is not namespace specific",
	[NVME_SC_FW_NEEDS_SUBSYS_RESET]		  = "Firmware Activation Requires NVM Subsystem Reset: The firmware commit was successful, however, activation of the firmware image requires an NVM Subsystem",
	[NVME_SC_FW_NEEDS_RESET]		  = "Firmware Activation Requires Controller Level Reset: The firmware commit was successful; however, the image specified does not support being activated without a reset",
	[NVME_SC_FW_NEEDS_MAX_TIME]		  = "Firmware Activation Requires Maximum Time Violation: The image specified if activated immediately would exceed the Maximum Time for Firmware Activation (MTFA) value reported in Identify Controller",
	[NVME_SC_FW_ACTIVATE_PROHIBITED]	  = "Firmware Activation Prohibited: The image specified is being prohibited from activation by the controller for vendor specific reasons",
	[NVME_SC_OVERLAPPING_RANGE]		  = "Overlapping Range: The downloaded firmware image has overlapping ranges",
	[NVME_SC_NS_INSUFFICIENT_CAP]		  = "Namespace Insufficient Capacity: Creating the namespace requires more free space than is currently available",
	[NVME_SC_NS_ID_UNAVAILABLE]		  = "Namespace Identifier Unavailable: The number of namespaces supported has been exceeded",
	[NVME_SC_NS_ALREADY_ATTACHED]		  = "Namespace Already Attached: The controller is already attached to the namespace specified",
	[NVME_SC_NS_IS_PRIVATE]			  = "Namespace Is Private: The namespace is private and is already attached to one controller",
	[NVME_SC_NS_NOT_ATTACHED]		  = "Namespace Not Attached: The request to detach the controller could not be completed because the controller is not attached to the namespace",
	[NVME_SC_THIN_PROV_NOT_SUPP]		  = "Thin Provisioning Not Supported: Thin provisioning is not supported by the controller",
	[NVME_SC_CTRL_LIST_INVALID]		  = "Controller List Invalid: The controller list provided contains invalid controller ids",
	[NVME_SC_SELF_TEST_IN_PROGRESS]		  = "Device Self-test In Progress: The controller or NVM subsystem already has a device self-test operation in process",
	[NVME_SC_BP_WRITE_PROHIBITED]		  = "Boot Partition Write Prohibited: The command tried to modify a locked Boot Partition",
	[NVME_SC_INVALID_CTRL_ID]		  = "Invalid Controller Identifier: An invalid controller id was specified",
	[NVME_SC_INVALID_SEC_CTRL_STATE]	  = "Invalid Secondary Controller State: The requested secondary controller action is invalid based on the secondary and primary controllers current states",
	[NVME_SC_INVALID_CTRL_RESOURCES]	  = "Invalid Number of Controller Resources: The specified number of Flexible Resources is invalid",
	[NVME_SC_INVALID_RESOURCE_ID]		  = "Invalid Resource Identifier: At least one of the specified resource identifiers was invalid",
	[NVME_SC_PMR_SAN_PROHIBITED]		  = "Sanitize Prohibited While Persistent Memory Region is Enabled",
	[NVME_SC_ANA_GROUP_ID_INVALID]		  = "ANA Group Identifier Invalid: The specified ANA Group Identifier (ANAGRPID) is not supported in the submitted command",
	[NVME_SC_ANA_ATTACH_FAILED]		  = "ANA Attach Failed: The controller is not attached to the namespace as a result of an ANA condition",
	[NVME_SC_INSUFFICIENT_CAP]		  = "Insufficient Capacity: Requested operation requires more free space than is currently available",
	[NVME_SC_NS_ATTACHMENT_LIMIT_EXCEEDED]	  = "Namespace Attachment Limit Exceeded: Attaching the ns to a controller causes max number of ns attachments allowed to be exceeded",
	[NVME_SC_PROHIBIT_CMD_EXEC_NOT_SUPPORTED] = "Prohibition of Command Execution Not Supported",
	[NVME_SC_IOCS_NOT_SUPPORTED]		  = "The I/O command set is not supported",
	[NVME_SC_IOCS_NOT_ENABLED]		  = "The I/O command set is not enabled",
	[NVME_SC_IOCS_COMBINATION_REJECTED]	  = "The I/O command set combination is rejected",
	[NVME_SC_INVALID_IOCS]			  = "The I/O command set is invalid",
	[NVME_SC_ID_UNAVAILABLE]		  = "Identifier Unavailable: The number of Endurance Groups or NVM Sets supported has been exceeded",
	[NVME_SC_INVALID_DISCOVERY_INFO]	  = "Discovery Info Entry not applicable to selected entity",
	[NVME_SC_ZONING_DATA_STRUCT_LOCKED]       = "The requested Zoning data structure is locked on the CDC",
	[NVME_SC_ZONING_DATA_STRUCT_NOTFND]       = "The requested Zoning data structure does not exist on the CDC",
	[NVME_SC_INSUFFICIENT_DISC_RES]		  = "Discovery Info entries exceed Discovery Controller's capacity",
	[NVME_SC_REQSTD_FUNCTION_DISABLED]        = "Fabric Zoning is not enabled on the CDC",
	[NVME_SC_ZONEGRP_ORIGINATOR_INVLD]        = "The NQN contained in the ZoneGroup Originator field does not match the Host NQN used by the DDC to connect to the CDC",
	[NVME_SC_INVALID_CONTROLER_DATA_QUEUE]	  = "Invalid Controller Data Queue",
	[NVME_SC_NOT_ENOUGH_RESOURCES]		  = "Not Enough Resources",
	[NVME_SC_CONTROLLER_SUSPENDED]		  = "Controller Suspended: Operation failed because the controller is currently in a suspended state",
	[NVME_SC_CONTROLLER_NOT_SUSPENDED]	  = "Controller Not Suspended: Operation failed because the controller is not in a suspended state",
	[NVME_SC_CONTROLLER_DATA_QUEUE_FULL]	  = "Controller Data Queue Full",
};

static const char * const nvm_status[] = {
	[NVME_SC_BAD_ATTRIBUTES]		 = "Conflicting Attributes: The attributes specified in the command are conflicting",
	[NVME_SC_INVALID_PI]			 = "Invalid Protection Information: The command's Protection Information Field settings are invalid for the namespace's Protection Information format",
	[NVME_SC_READ_ONLY]			 = "Attempted Write to Read Only Range: The LBA range specified contains read-only blocks",
	[NVME_SC_CMD_SIZE_LIMIT_EXCEEDED]	 = "Command Size Limit Exceeded",
	[NVME_SC_INCOMPATIBLE_NS]		 = "Incompatible Namespace or Format",
	[NVME_SC_FAST_COPY_NOT_POSSIBLE]	 = "Fast Copy Not Possible",
	[NVME_SC_OVERLAPPING_IO_RANGE]		 = "Overlapping I/O Range",
	[NVME_SC_INSUFFICIENT_RESOURCES]	 = "Insufficient Resources",
	[NVME_SC_ZNS_INVALID_OP_REQUEST]	 = "Invalid Zone Operation Request: The operation requested is invalid",
	[NVME_SC_ZNS_ZRWA_RESOURCES_UNAVAILABLE] = "ZRWA Resources Unavailable: No ZRWAs are available",
	[NVME_SC_ZNS_BOUNDARY_ERROR]		 = "Zoned Boundary Error: Invalid Zone Boundary crossing",
	[NVME_SC_ZNS_FULL]			 = "Zone Is Full: The accessed zone is in ZSF:Full state",
	[NVME_SC_ZNS_READ_ONLY]			 = "Zone Is Read Only: The accessed zone is in ZSRO:Read Only state",
	[NVME_SC_ZNS_OFFLINE]			 = "Zone Is Offline: The access zone is in ZSO:Offline state",
	[NVME_SC_ZNS_INVALID_WRITE]		 = "Zone Invalid Write: The write to zone was not at the write pointer offset",
	[NVME_SC_ZNS_TOO_MANY_ACTIVE]		 = "Too Many Active Zones: The controller does not allow additional active zones",
	[NVME_SC_ZNS_TOO_MANY_OPENS]		 = "Too Many Open Zones: The controller does not allow additional open zones",
	[NVME_SC_ZNS_INVAL_TRANSITION]		 = "Invalid Zone State Transition: The request is not a valid zone state transition",
};

static const char * const nvmf_status[] = {
	[NVME_SC_CONNECT_FORMAT]	   = "Incompatible Format: The NVM subsystem does not support the record format specified by the host",
	[NVME_SC_CONNECT_CTRL_BUSY]	   = "Controller Busy: The controller is already associated with a host",
	[NVME_SC_CONNECT_INVALID_PARAM]    = "Connect Invalid Parameters: One or more of the command parameters",
	[NVME_SC_CONNECT_RESTART_DISC]	   = "Connect Restart Discovery: The NVM subsystem requested is not available",
	[NVME_SC_CONNECT_INVALID_HOST]	   = "Connect Invalid Host: The host is not allowed to establish an association to either any controller in the NVM subsystem or the specified controller",
	[NVME_SC_DISCONNECT_INVALID_QTYPE] = "Invalid Queue Type: The command was sent on the wrong queue type",
	[NVME_SC_DISCOVERY_RESTART]	   = "Discover Restart: The snapshot of the records is now invalid or out of date",
	[NVME_SC_AUTH_REQUIRED]		   = "Authentication Required: NVMe in-band authentication is required and the queue has not yet been authenticated",
};

static const char * const media_status[] = {
	[NVME_SC_WRITE_FAULT]		 = "Write Fault: The write data could not be committed to the media",
	[NVME_SC_READ_ERROR]		 = "Unrecovered Read Error: The read data could not be recovered from the media",
	[NVME_SC_GUARD_CHECK]		 = "End-to-end Guard Check Error: The command was aborted due to an end-to-end guard check failure",
	[NVME_SC_APPTAG_CHECK]		 = "End-to-end Application Tag Check Error: The command was aborted due to an end-to-end application tag check failure",
	[NVME_SC_REFTAG_CHECK]		 = "End-to-end Reference Tag Check Error: The command was aborted due to an end-to-end reference tag check failure",
	[NVME_SC_COMPARE_FAILED]	 = "Compare Failure: The command failed due to a miscompare during a Compare command",
	[NVME_SC_ACCESS_DENIED]		 = "Access Denied: Access to the namespace and/or LBA range is denied due to lack of access rights",
	[NVME_SC_UNWRITTEN_BLOCK]	 = "Deallocated or Unwritten Logical Block: The command failed due to an attempt to read from or verify an LBA range containing a deallocated or unwritten logical block",
	[NVME_SC_STORAGE_TAG_CHECK]	 = "End-to-End Storage Tag Check Error: The command was aborted due to an end-to-end storage tag check failure",
};

static const char * const path_status[] = {
	[NVME_SC_ANA_INTERNAL_PATH_ERROR] = "Internal Path Error: An internal error specific to the controller processing the command prevented completion",
	[NVME_SC_ANA_PERSISTENT_LOSS]	  = "Asymmetric Access Persistent Loss: The controller is in a persistent loss state with the requested namespace",
	[NVME_SC_ANA_INACCESSIBLE]	  = "Asymmetric Access Inaccessible: The controller is in an inaccessible state with the requested namespace",
	[NVME_SC_ANA_TRANSITION]	  = "Asymmetric Access Transition: The controller is currently transitioning states with the requested namespace",
	[NVME_SC_CTRL_PATH_ERROR]	  = "Controller Pathing Error: A pathing error was detected by the controller",
	[NVME_SC_HOST_PATH_ERROR]	  = "Host Pathing Error: A pathing error was detected by the host",
	[NVME_SC_CMD_ABORTED_BY_HOST]	  = "Command Aborted By Host: The command was aborted as a result of host action",
};

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define ARGSTR(s, i) arg_str(s, ARRAY_SIZE(s), i)

static const char *arg_str(const char * const *strings,
		size_t array_size, size_t idx)
{
	if (idx < array_size && strings[idx])
		return strings[idx];
	return "unrecognized";
}

__shr_public const char *libnvme_status_to_string(int status, bool fabrics)
{
	const char *s = "Unknown status";
	__u16 sc, sct;

	if (status < 0)
		return strerror(-status);

	sc = nvme_status_code(status);
	sct = nvme_status_code_type(status);

	switch (sct) {
	case NVME_SCT_GENERIC:
		s = ARGSTR(generic_status, sc);
		break;
	case NVME_SCT_CMD_SPECIFIC:
		if (sc < ARRAY_SIZE(cmd_spec_status))
			s = ARGSTR(cmd_spec_status, sc);
		else if (fabrics)
			s = ARGSTR(nvmf_status, sc);
		else
			s = ARGSTR(nvm_status, sc);
		break;
	case NVME_SCT_MEDIA:
		s = ARGSTR(media_status, sc);
		break;
	case NVME_SCT_PATH:
		s = ARGSTR(path_status, sc);
		break;
	case NVME_SCT_VS:
		s = "Vendor Specific Status";
		break;
	default:
		break;
	}

	return s;
}

static const char * const libnvme_status[] = {
	[ENVME_CONNECT_RESOLVE] = "failed to resolve host",
	[ENVME_CONNECT_ADDRFAM] = "unrecognized address family",
	[ENVME_CONNECT_TRADDR] = "failed to get transport address",
	[ENVME_CONNECT_TARG] = "no transport specified",
	[ENVME_CONNECT_AARG] = "no transport address specified",
	[ENVME_CONNECT_OPEN] = "failed to open nvme-fabrics device",
	[ENVME_CONNECT_WRITE] = "failed to write to nvme-fabrics device",
	[ENVME_CONNECT_READ] = "failed to read from nvme-fabrics device",
	[ENVME_CONNECT_PARSE] = "failed to parse ctrl info",
	[ENVME_CONNECT_INVAL_TR] = "invalid transport type",
	[ENVME_CONNECT_LOOKUP_SUBSYS_NAME] = "failed to lookup subsystem name",
	[ENVME_CONNECT_LOOKUP_SUBSYS] = "failed to lookup subsystem",
	[ENVME_CONNECT_ALREADY] = "already connected",
	[ENVME_CONNECT_INVAL] = "invalid arguments/configuration",
	[ENVME_CONNECT_ADDRINUSE] = "hostnqn already in use",
	[ENVME_CONNECT_NODEV] = "invalid interface",
	[ENVME_CONNECT_OPNOTSUPP] = "not supported",
	[ENVME_CONNECT_CONNREFUSED] = "connection refused",
	[ENVME_CONNECT_ADDRNOTAVAIL] = "cannot assign requested address",
	[ENVME_CONNECT_IGNORED] = "connection ignored",
	[ENVME_CONNECT_NOKEY] = "TLS PSK or KX-HMAC-CHAP secret not available"
};

__shr_public const char *libnvme_errno_to_string(int status)
{
	const char *s = ARGSTR(libnvme_status, status);

	return s;
}

__shr_public const char *libnvme_strerror(int errnum)
{
	if (errnum >= ENVME_CONNECT_RESOLVE)
		return libnvme_errno_to_string(errnum);
	return strerror(errnum);
}

static const char *const mi_status[] = {
	[NVME_MI_RESP_SUCCESS]               = "Success",
	[NVME_MI_RESP_MPR]                   = "More Processing Required: The command message is in progress and requires more time to complete processing",
	[NVME_MI_RESP_INTERNAL_ERR]          = "Internal Error: The request message could not be processed due to a vendor-specific error",
	[NVME_MI_RESP_INVALID_OPCODE]        = "Invalid Command Opcode",
	[NVME_MI_RESP_INVALID_PARAM]         = "Invalid Parameter",
	[NVME_MI_RESP_INVALID_CMD_SIZE]      = "Invalid Command Size: The size of the message body of the request was different than expected",
	[NVME_MI_RESP_INVALID_INPUT_SIZE]    = "Invalid Command Input Data Size: The command requires data and contains too much or too little data",
	[NVME_MI_RESP_ACCESS_DENIED]         = "Access Denied. Processing prohibited due to a vendor-specific mechanism of the Command and Feature lockdown function",
	[NVME_MI_RESP_VPD_UPDATES_EXCEEDED]  = "VPD Updates Exceeded",
	[NVME_MI_RESP_PCIE_INACCESSIBLE]     = "PCIe Inaccessible. The PCIe functionality is not available at this time",
	[NVME_MI_RESP_MEB_SANITIZED]         = "Management Endpoint Buffer Cleared Due to Sanitize",
	[NVME_MI_RESP_ENC_SERV_FAILURE]      = "Enclosure Services Failure",
	[NVME_MI_RESP_ENC_SERV_XFER_FAILURE] = "Enclosure Services Transfer Failure: Communication with the Enclosure Services Process has failed",
	[NVME_MI_RESP_ENC_FAILURE]           = "An unrecoverable enclosure failure has been detected by the Enclosure Services Process",
	[NVME_MI_RESP_ENC_XFER_REFUSED]      = "Enclosure Services Transfer Refused: The NVM Subsystem or Enclosure Services Process indicated an error or an invalid format in communication",
	[NVME_MI_RESP_ENC_FUNC_UNSUP]        = "Unsupported Enclosure Function: An SES Send command has been attempted to a simple Subenclosure",
	[NVME_MI_RESP_ENC_SERV_UNAVAIL]      = "Enclosure Services Unavailable: The NVM Subsystem or Enclosure Services Process has encountered an error but may become available again",
	[NVME_MI_RESP_ENC_DEGRADED]          = "Enclosure Degraded: A noncritical failure has been detected by the Enclosure Services Process",
	[NVME_MI_RESP_SANITIZE_IN_PROGRESS]  = "Sanitize In Progress: The requested command is prohibited while a sanitize operation is in progress",
};

__shr_public const char *libnvme_mi_status_to_string(int status)
{
	const char *s = "Unknown status";

	if (status < ARRAY_SIZE(mi_status) && mi_status[status])
		s = mi_status[status];

	return s;
}

__shr_public const char *libnvme_get_version(enum libnvme_version type)
{
	switch(type) {
	case LIBNVME_VERSION_PROJECT:
		return LIBNVME_VERSION;
	case LIBNVME_VERSION_GIT:
		return GIT_VERSION;
	default:
		return "n/a";
	}
}

__shr_public int libnvme_uuid_to_string(
		unsigned char uuid[NVME_UUID_LEN], char *str)
{
	int n;
	n = snprintf(str, NVME_UUID_LEN_STRING,
		     "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
		     "%02x%02x-%02x%02x%02x%02x%02x%02x",
		     uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
		     uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
		     uuid[12], uuid[13], uuid[14], uuid[15]);
	return n != NVME_UUID_LEN_STRING - 1 ? -EINVAL : 0;
}

__shr_public int libnvme_uuid_from_string(
		const char *str, unsigned char uuid[NVME_UUID_LEN])
{
	int n;

	n = sscanf(str,
		   "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-"
		   "%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
		   &uuid[0], &uuid[1], &uuid[2], &uuid[3], &uuid[4], &uuid[5],
		   &uuid[6], &uuid[7], &uuid[8], &uuid[9], &uuid[10], &uuid[11],
		   &uuid[12], &uuid[13], &uuid[14], &uuid[15]);
	return n != NVME_UUID_LEN ? -EINVAL : 0;

}

static int random_bytes(void *buf, size_t buflen)
{
#if NVME_HAVE_BCRYPT
	NTSTATUS status = BCryptGenRandom(NULL, buf, (ULONG)buflen,
				 BCRYPT_USE_SYSTEM_PREFERRED_RNG);

	if (!BCRYPT_SUCCESS(status))
		return -EIO;
#else
	__cleanup_fd int fd = -1;
	ssize_t n;

	fd = open("/dev/urandom", O_RDONLY);
	if (fd < 0)
		return -errno;

	n = read(fd, buf, buflen);
	if (n < 0)
		return -errno;
	else if ((size_t)n != buflen)
		return -EIO;
#endif
	return 0;
}

__shr_public int libnvme_random_uuid(unsigned char uuid[NVME_UUID_LEN])
{
	int ret;

	/* Generate random bytes using platform-specific implementation */
	ret = random_bytes(uuid, NVME_UUID_LEN);
	if (ret < 0)
		return ret;

	/*
	 * See https://www.rfc-editor.org/rfc/rfc4122#section-4.4
	 * Algorithms for Creating a UUID from Truly Random
	 * or Pseudo-Random Numbers
	 */
	uuid[6] = (uuid[6] & 0x0f) | 0x40;
	uuid[8] = (uuid[8] & 0x3f) | 0x80;

	return 0;
}

__shr_public int libnvme_find_uuid(struct nvme_id_uuid_list *uuid_list,
		const unsigned char uuid[NVME_UUID_LEN])
{
	const unsigned char uuid_end[NVME_UUID_LEN] = {0};

	if ((!uuid_list) || (!uuid))
		return -EINVAL;

	for (int i = 0; i < NVME_ID_UUID_LIST_MAX; i++) {
		if (memcmp(uuid, &uuid_list->entry[i].uuid, NVME_UUID_LEN) == 0)
			return i + 1;
		if (memcmp(uuid_end, &uuid_list->entry[i].uuid, NVME_UUID_LEN) == 0)
			break;
	}
	return -ENOENT;
}

#ifdef CONFIG_FABRICS
bool libnvme_ipaddrs_eq(const char *addr1, const char *addr2)
{
	return shr_ipaddrs_eq(addr1, addr2);
}
#else /* CONFIG_FABRICS */
bool libnvme_ipaddrs_eq(const char *addr1, const char *addr2)
{
	return false;
}
#endif /* CONFIG_FABRICS */

#ifdef CONFIG_FABRICS
const char *libnvme_iface_matching_addr(const struct ifaddrs *iface_list,
		const char *addr)
{
	return shr_iface_matching_addr(iface_list, addr);
}

bool libnvme_iface_primary_addr_matches(const struct ifaddrs *iface_list,
		const char *iface, const char *addr)
{
	return shr_iface_primary_addr_matches(iface_list, iface, addr);
}
#endif /* CONFIG_FABRICS */

/*
 * libnvme_fabrics_config currently contains only scalar fields, so a
 * shallow copy is correct. This function exists as a single point of
 * change: if a string or pointer field is ever added, update only this
 * function to perform a deep copy rather than patching every call site.
 */
void libnvme_fabrics_config_copy(struct libnvme_fabrics_config *dst,
		const struct libnvme_fabrics_config *src)
{
	*dst = *src;
}
