errors.c 8.4 KB

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