brintos

brintos / linux-shallow public Read only

0
0
Text · 8.4 KiB · 6f89eb1 Raw
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