| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright 2023 Red Hat
- */
- #include "errors.h"
- #include <linux/compiler.h>
- #include <linux/errno.h>
- #include "logger.h"
- #include "permassert.h"
- #include "string-utils.h"
- static const struct error_info successful = { "UDS_SUCCESS", "Success" };
- static const char *const message_table[] = {
- [EPERM] = "Operation not permitted",
- [ENOENT] = "No such file or directory",
- [ESRCH] = "No such process",
- [EINTR] = "Interrupted system call",
- [EIO] = "Input/output error",
- [ENXIO] = "No such device or address",
- [E2BIG] = "Argument list too long",
- [ENOEXEC] = "Exec format error",
- [EBADF] = "Bad file descriptor",
- [ECHILD] = "No child processes",
- [EAGAIN] = "Resource temporarily unavailable",
- [ENOMEM] = "Cannot allocate memory",
- [EACCES] = "Permission denied",
- [EFAULT] = "Bad address",
- [ENOTBLK] = "Block device required",
- [EBUSY] = "Device or resource busy",
- [EEXIST] = "File exists",
- [EXDEV] = "Invalid cross-device link",
- [ENODEV] = "No such device",
- [ENOTDIR] = "Not a directory",
- [EISDIR] = "Is a directory",
- [EINVAL] = "Invalid argument",
- [ENFILE] = "Too many open files in system",
- [EMFILE] = "Too many open files",
- [ENOTTY] = "Inappropriate ioctl for device",
- [ETXTBSY] = "Text file busy",
- [EFBIG] = "File too large",
- [ENOSPC] = "No space left on device",
- [ESPIPE] = "Illegal seek",
- [EROFS] = "Read-only file system",
- [EMLINK] = "Too many links",
- [EPIPE] = "Broken pipe",
- [EDOM] = "Numerical argument out of domain",
- [ERANGE] = "Numerical result out of range"
- };
- static const struct error_info error_list[] = {
- { "UDS_OVERFLOW", "Index overflow" },
- { "UDS_INVALID_ARGUMENT", "Invalid argument passed to internal routine" },
- { "UDS_BAD_STATE", "UDS data structures are in an invalid state" },
- { "UDS_DUPLICATE_NAME", "Attempt to enter the same name into a delta index twice" },
- { "UDS_ASSERTION_FAILED", "Assertion failed" },
- { "UDS_QUEUED", "Request queued" },
- { "UDS_ALREADY_REGISTERED", "Error range already registered" },
- { "UDS_OUT_OF_RANGE", "Cannot access data outside specified limits" },
- { "UDS_DISABLED", "UDS library context is disabled" },
- { "UDS_UNSUPPORTED_VERSION", "Unsupported version" },
- { "UDS_CORRUPT_DATA", "Some index structure is corrupt" },
- { "UDS_NO_INDEX", "No index found" },
- { "UDS_INDEX_NOT_SAVED_CLEANLY", "Index not saved cleanly" },
- };
- struct error_block {
- const char *name;
- int base;
- int last;
- int max;
- const struct error_info *infos;
- };
- #define MAX_ERROR_BLOCKS 6
- static struct {
- int allocated;
- int count;
- struct error_block blocks[MAX_ERROR_BLOCKS];
- } registered_errors = {
- .allocated = MAX_ERROR_BLOCKS,
- .count = 1,
- .blocks = { {
- .name = "UDS Error",
- .base = UDS_ERROR_CODE_BASE,
- .last = UDS_ERROR_CODE_LAST,
- .max = UDS_ERROR_CODE_BLOCK_END,
- .infos = error_list,
- } },
- };
- /* Get the error info for an error number. Also returns the name of the error block, if known. */
- static const char *get_error_info(int errnum, const struct error_info **info_ptr)
- {
- struct error_block *block;
- if (errnum == UDS_SUCCESS) {
- *info_ptr = &successful;
- return NULL;
- }
- for (block = registered_errors.blocks;
- block < registered_errors.blocks + registered_errors.count;
- block++) {
- if ((errnum >= block->base) && (errnum < block->last)) {
- *info_ptr = block->infos + (errnum - block->base);
- return block->name;
- } else if ((errnum >= block->last) && (errnum < block->max)) {
- *info_ptr = NULL;
- return block->name;
- }
- }
- return NULL;
- }
- /* Return a string describing a system error message. */
- static const char *system_string_error(int errnum, char *buf, size_t buflen)
- {
- size_t len;
- const char *error_string = NULL;
- if ((errnum > 0) && (errnum < ARRAY_SIZE(message_table)))
- error_string = message_table[errnum];
- len = ((error_string == NULL) ?
- snprintf(buf, buflen, "Unknown error %d", errnum) :
- snprintf(buf, buflen, "%s", error_string));
- if (len < buflen)
- return buf;
- buf[0] = '\0';
- return "System error";
- }
- /* Convert an error code to a descriptive string. */
- const char *uds_string_error(int errnum, char *buf, size_t buflen)
- {
- char *buffer = buf;
- char *buf_end = buf + buflen;
- const struct error_info *info = NULL;
- const char *block_name;
- if (buf == NULL)
- return NULL;
- if (errnum < 0)
- errnum = -errnum;
- block_name = get_error_info(errnum, &info);
- if (block_name != NULL) {
- if (info != NULL) {
- buffer = vdo_append_to_buffer(buffer, buf_end, "%s: %s",
- block_name, info->message);
- } else {
- buffer = vdo_append_to_buffer(buffer, buf_end, "Unknown %s %d",
- block_name, errnum);
- }
- } else if (info != NULL) {
- buffer = vdo_append_to_buffer(buffer, buf_end, "%s", info->message);
- } else {
- const char *tmp = system_string_error(errnum, buffer, buf_end - buffer);
- if (tmp != buffer)
- buffer = vdo_append_to_buffer(buffer, buf_end, "%s", tmp);
- else
- buffer += strlen(tmp);
- }
- return buf;
- }
- /* Convert an error code to its name. */
- const char *uds_string_error_name(int errnum, char *buf, size_t buflen)
- {
- char *buffer = buf;
- char *buf_end = buf + buflen;
- const struct error_info *info = NULL;
- const char *block_name;
- if (errnum < 0)
- errnum = -errnum;
- block_name = get_error_info(errnum, &info);
- if (block_name != NULL) {
- if (info != NULL) {
- buffer = vdo_append_to_buffer(buffer, buf_end, "%s", info->name);
- } else {
- buffer = vdo_append_to_buffer(buffer, buf_end, "%s %d",
- block_name, errnum);
- }
- } else if (info != NULL) {
- buffer = vdo_append_to_buffer(buffer, buf_end, "%s", info->name);
- } else {
- const char *tmp;
- tmp = system_string_error(errnum, buffer, buf_end - buffer);
- if (tmp != buffer)
- buffer = vdo_append_to_buffer(buffer, buf_end, "%s", tmp);
- else
- buffer += strlen(tmp);
- }
- return buf;
- }
- /*
- * Translate an error code into a value acceptable to the kernel. The input error code may be a
- * system-generated value (such as -EIO), or an internal UDS status code. The result will be a
- * negative errno value.
- */
- int uds_status_to_errno(int error)
- {
- char error_name[VDO_MAX_ERROR_NAME_SIZE];
- char error_message[VDO_MAX_ERROR_MESSAGE_SIZE];
- /* 0 is success, and negative values are already system error codes. */
- if (likely(error <= 0))
- return error;
- if (error < 1024) {
- /* This is probably an errno from userspace. */
- return -error;
- }
- /* Internal UDS errors */
- switch (error) {
- case UDS_NO_INDEX:
- case UDS_CORRUPT_DATA:
- /* The index doesn't exist or can't be recovered. */
- return -ENOENT;
- case UDS_INDEX_NOT_SAVED_CLEANLY:
- case UDS_UNSUPPORTED_VERSION:
- /*
- * The index exists, but can't be loaded. Tell the client it exists so they don't
- * destroy it inadvertently.
- */
- return -EEXIST;
- case UDS_DISABLED:
- /* The session is unusable; only returned by requests. */
- return -EIO;
- default:
- /* Translate an unexpected error into something generic. */
- vdo_log_info("%s: mapping status code %d (%s: %s) to -EIO",
- __func__, error,
- uds_string_error_name(error, error_name,
- sizeof(error_name)),
- uds_string_error(error, error_message,
- sizeof(error_message)));
- return -EIO;
- }
- }
- /*
- * Register a block of error codes.
- *
- * @block_name: the name of the block of error codes
- * @first_error: the first error code in the block
- * @next_free_error: one past the highest possible error in the block
- * @infos: a pointer to the error info array for the block
- * @info_size: the size of the error info array
- */
- int uds_register_error_block(const char *block_name, int first_error,
- int next_free_error, const struct error_info *infos,
- size_t info_size)
- {
- int result;
- struct error_block *block;
- struct error_block new_block = {
- .name = block_name,
- .base = first_error,
- .last = first_error + (info_size / sizeof(struct error_info)),
- .max = next_free_error,
- .infos = infos,
- };
- result = VDO_ASSERT(first_error < next_free_error,
- "well-defined error block range");
- if (result != VDO_SUCCESS)
- return result;
- if (registered_errors.count == registered_errors.allocated) {
- /* This should never happen. */
- return UDS_OVERFLOW;
- }
- for (block = registered_errors.blocks;
- block < registered_errors.blocks + registered_errors.count;
- block++) {
- if (strcmp(block_name, block->name) == 0)
- return UDS_DUPLICATE_NAME;
- /* Ensure error ranges do not overlap. */
- if ((first_error < block->max) && (next_free_error > block->base))
- return UDS_ALREADY_REGISTERED;
- }
- registered_errors.blocks[registered_errors.count++] = new_block;
- return UDS_SUCCESS;
- }
|