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. */
get_error_info(int errnum,const struct error_info ** info_ptr)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. */
system_string_error(int errnum,char * buf,size_t buflen)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. */
uds_string_error(int errnum,char * buf,size_t buflen)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. */
uds_string_error_name(int errnum,char * buf,size_t buflen)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  */
uds_status_to_errno(int error)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  */
uds_register_error_block(const char * block_name,int first_error,int next_free_error,const struct error_info * infos,size_t info_size)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