1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2023 Red Hat 4 */ 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 15 static const struct error_info successful = { "UDS_SUCCESS", "Success" }; 16 17 static 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 54 static 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 70 struct 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 6 79 80 static 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. */ 97 static 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. */ 122 static 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. */ 141 const 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 else 171 buffer += strlen(tmp); 172 } 173 174 return buf; 175 } 176 177 /* Convert an error code to its name. */ 178 const 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 else 205 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 a 213 * system-generated value (such as -EIO), or an internal UDS status code. The result will be a 214 * negative errno value. 215 */ 216 int 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't 241 * 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 codes 265 * @first_error: the first error code in the block 266 * @next_free_error: one past the highest possible error in the block 267 * @infos: a pointer to the error info array for the block 268 * @info_size: the size of the error info array 269 */ 270 int 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