308 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2023 Red Hat4 */5 6#include "errors.h"7 8#include <linux/compiler.h>9#include <linux/errno.h>10 11#include "logger.h"12#include "permassert.h"13#include "string-utils.h"14 15static const struct error_info successful = { "UDS_SUCCESS", "Success" };16 17static const char *const message_table[] = {18 [EPERM] = "Operation not permitted",19 [ENOENT] = "No such file or directory",20 [ESRCH] = "No such process",21 [EINTR] = "Interrupted system call",22 [EIO] = "Input/output error",23 [ENXIO] = "No such device or address",24 [E2BIG] = "Argument list too long",25 [ENOEXEC] = "Exec format error",26 [EBADF] = "Bad file descriptor",27 [ECHILD] = "No child processes",28 [EAGAIN] = "Resource temporarily unavailable",29 [ENOMEM] = "Cannot allocate memory",30 [EACCES] = "Permission denied",31 [EFAULT] = "Bad address",32 [ENOTBLK] = "Block device required",33 [EBUSY] = "Device or resource busy",34 [EEXIST] = "File exists",35 [EXDEV] = "Invalid cross-device link",36 [ENODEV] = "No such device",37 [ENOTDIR] = "Not a directory",38 [EISDIR] = "Is a directory",39 [EINVAL] = "Invalid argument",40 [ENFILE] = "Too many open files in system",41 [EMFILE] = "Too many open files",42 [ENOTTY] = "Inappropriate ioctl for device",43 [ETXTBSY] = "Text file busy",44 [EFBIG] = "File too large",45 [ENOSPC] = "No space left on device",46 [ESPIPE] = "Illegal seek",47 [EROFS] = "Read-only file system",48 [EMLINK] = "Too many links",49 [EPIPE] = "Broken pipe",50 [EDOM] = "Numerical argument out of domain",51 [ERANGE] = "Numerical result out of range"52};53 54static const struct error_info error_list[] = {55 { "UDS_OVERFLOW", "Index overflow" },56 { "UDS_INVALID_ARGUMENT", "Invalid argument passed to internal routine" },57 { "UDS_BAD_STATE", "UDS data structures are in an invalid state" },58 { "UDS_DUPLICATE_NAME", "Attempt to enter the same name into a delta index twice" },59 { "UDS_ASSERTION_FAILED", "Assertion failed" },60 { "UDS_QUEUED", "Request queued" },61 { "UDS_ALREADY_REGISTERED", "Error range already registered" },62 { "UDS_OUT_OF_RANGE", "Cannot access data outside specified limits" },63 { "UDS_DISABLED", "UDS library context is disabled" },64 { "UDS_UNSUPPORTED_VERSION", "Unsupported version" },65 { "UDS_CORRUPT_DATA", "Some index structure is corrupt" },66 { "UDS_NO_INDEX", "No index found" },67 { "UDS_INDEX_NOT_SAVED_CLEANLY", "Index not saved cleanly" },68};69 70struct error_block {71 const char *name;72 int base;73 int last;74 int max;75 const struct error_info *infos;76};77 78#define MAX_ERROR_BLOCKS 679 80static struct {81 int allocated;82 int count;83 struct error_block blocks[MAX_ERROR_BLOCKS];84} registered_errors = {85 .allocated = MAX_ERROR_BLOCKS,86 .count = 1,87 .blocks = { {88 .name = "UDS Error",89 .base = UDS_ERROR_CODE_BASE,90 .last = UDS_ERROR_CODE_LAST,91 .max = UDS_ERROR_CODE_BLOCK_END,92 .infos = error_list,93 } },94};95 96/* Get the error info for an error number. Also returns the name of the error block, if known. */97static const char *get_error_info(int errnum, const struct error_info **info_ptr)98{99 struct error_block *block;100 101 if (errnum == UDS_SUCCESS) {102 *info_ptr = &successful;103 return NULL;104 }105 106 for (block = registered_errors.blocks;107 block < registered_errors.blocks + registered_errors.count;108 block++) {109 if ((errnum >= block->base) && (errnum < block->last)) {110 *info_ptr = block->infos + (errnum - block->base);111 return block->name;112 } else if ((errnum >= block->last) && (errnum < block->max)) {113 *info_ptr = NULL;114 return block->name;115 }116 }117 118 return NULL;119}120 121/* Return a string describing a system error message. */122static const char *system_string_error(int errnum, char *buf, size_t buflen)123{124 size_t len;125 const char *error_string = NULL;126 127 if ((errnum > 0) && (errnum < ARRAY_SIZE(message_table)))128 error_string = message_table[errnum];129 130 len = ((error_string == NULL) ?131 snprintf(buf, buflen, "Unknown error %d", errnum) :132 snprintf(buf, buflen, "%s", error_string));133 if (len < buflen)134 return buf;135 136 buf[0] = '\0';137 return "System error";138}139 140/* Convert an error code to a descriptive string. */141const char *uds_string_error(int errnum, char *buf, size_t buflen)142{143 char *buffer = buf;144 char *buf_end = buf + buflen;145 const struct error_info *info = NULL;146 const char *block_name;147 148 if (buf == NULL)149 return NULL;150 151 if (errnum < 0)152 errnum = -errnum;153 154 block_name = get_error_info(errnum, &info);155 if (block_name != NULL) {156 if (info != NULL) {157 buffer = vdo_append_to_buffer(buffer, buf_end, "%s: %s",158 block_name, info->message);159 } else {160 buffer = vdo_append_to_buffer(buffer, buf_end, "Unknown %s %d",161 block_name, errnum);162 }163 } else if (info != NULL) {164 buffer = vdo_append_to_buffer(buffer, buf_end, "%s", info->message);165 } else {166 const char *tmp = system_string_error(errnum, buffer, buf_end - buffer);167 168 if (tmp != buffer)169 buffer = vdo_append_to_buffer(buffer, buf_end, "%s", tmp);170 else171 buffer += strlen(tmp);172 }173 174 return buf;175}176 177/* Convert an error code to its name. */178const char *uds_string_error_name(int errnum, char *buf, size_t buflen)179{180 char *buffer = buf;181 char *buf_end = buf + buflen;182 const struct error_info *info = NULL;183 const char *block_name;184 185 if (errnum < 0)186 errnum = -errnum;187 188 block_name = get_error_info(errnum, &info);189 if (block_name != NULL) {190 if (info != NULL) {191 buffer = vdo_append_to_buffer(buffer, buf_end, "%s", info->name);192 } else {193 buffer = vdo_append_to_buffer(buffer, buf_end, "%s %d",194 block_name, errnum);195 }196 } else if (info != NULL) {197 buffer = vdo_append_to_buffer(buffer, buf_end, "%s", info->name);198 } else {199 const char *tmp;200 201 tmp = system_string_error(errnum, buffer, buf_end - buffer);202 if (tmp != buffer)203 buffer = vdo_append_to_buffer(buffer, buf_end, "%s", tmp);204 else205 buffer += strlen(tmp);206 }207 208 return buf;209}210 211/*212 * Translate an error code into a value acceptable to the kernel. The input error code may be a213 * system-generated value (such as -EIO), or an internal UDS status code. The result will be a214 * negative errno value.215 */216int uds_status_to_errno(int error)217{218 char error_name[VDO_MAX_ERROR_NAME_SIZE];219 char error_message[VDO_MAX_ERROR_MESSAGE_SIZE];220 221 /* 0 is success, and negative values are already system error codes. */222 if (likely(error <= 0))223 return error;224 225 if (error < 1024) {226 /* This is probably an errno from userspace. */227 return -error;228 }229 230 /* Internal UDS errors */231 switch (error) {232 case UDS_NO_INDEX:233 case UDS_CORRUPT_DATA:234 /* The index doesn't exist or can't be recovered. */235 return -ENOENT;236 237 case UDS_INDEX_NOT_SAVED_CLEANLY:238 case UDS_UNSUPPORTED_VERSION:239 /*240 * The index exists, but can't be loaded. Tell the client it exists so they don't241 * destroy it inadvertently.242 */243 return -EEXIST;244 245 case UDS_DISABLED:246 /* The session is unusable; only returned by requests. */247 return -EIO;248 249 default:250 /* Translate an unexpected error into something generic. */251 vdo_log_info("%s: mapping status code %d (%s: %s) to -EIO",252 __func__, error,253 uds_string_error_name(error, error_name,254 sizeof(error_name)),255 uds_string_error(error, error_message,256 sizeof(error_message)));257 return -EIO;258 }259}260 261/*262 * Register a block of error codes.263 *264 * @block_name: the name of the block of error codes265 * @first_error: the first error code in the block266 * @next_free_error: one past the highest possible error in the block267 * @infos: a pointer to the error info array for the block268 * @info_size: the size of the error info array269 */270int uds_register_error_block(const char *block_name, int first_error,271 int next_free_error, const struct error_info *infos,272 size_t info_size)273{274 int result;275 struct error_block *block;276 struct error_block new_block = {277 .name = block_name,278 .base = first_error,279 .last = first_error + (info_size / sizeof(struct error_info)),280 .max = next_free_error,281 .infos = infos,282 };283 284 result = VDO_ASSERT(first_error < next_free_error,285 "well-defined error block range");286 if (result != VDO_SUCCESS)287 return result;288 289 if (registered_errors.count == registered_errors.allocated) {290 /* This should never happen. */291 return UDS_OVERFLOW;292 }293 294 for (block = registered_errors.blocks;295 block < registered_errors.blocks + registered_errors.count;296 block++) {297 if (strcmp(block_name, block->name) == 0)298 return UDS_DUPLICATE_NAME;299 300 /* Ensure error ranges do not overlap. */301 if ((first_error < block->max) && (next_free_error > block->base))302 return UDS_ALREADY_REGISTERED;303 }304 305 registered_errors.blocks[registered_errors.count++] = new_block;306 return UDS_SUCCESS;307}308