1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2023 Red Hat
8 #include <linux/compiler.h>
9 #include <linux/errno.h>
12 #include "permassert.h"
13 #include "string-utils.h"
15 static const struct error_info successful
= { "UDS_SUCCESS", "Success" };
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"
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" },
75 const struct error_info
*infos
;
78 #define MAX_ERROR_BLOCKS 6
83 struct error_block blocks
[MAX_ERROR_BLOCKS
];
84 } registered_errors
= {
85 .allocated
= MAX_ERROR_BLOCKS
,
89 .base
= UDS_ERROR_CODE_BASE
,
90 .last
= UDS_ERROR_CODE_LAST
,
91 .max
= UDS_ERROR_CODE_BLOCK_END
,
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
)
99 struct error_block
*block
;
101 if (errnum
== UDS_SUCCESS
) {
102 *info_ptr
= &successful
;
106 for (block
= registered_errors
.blocks
;
107 block
< registered_errors
.blocks
+ registered_errors
.count
;
109 if ((errnum
>= block
->base
) && (errnum
< block
->last
)) {
110 *info_ptr
= block
->infos
+ (errnum
- block
->base
);
112 } else if ((errnum
>= block
->last
) && (errnum
< block
->max
)) {
121 /* Return a string describing a system error message. */
122 static const char *system_string_error(int errnum
, char *buf
, size_t buflen
)
125 const char *error_string
= NULL
;
127 if ((errnum
> 0) && (errnum
< ARRAY_SIZE(message_table
)))
128 error_string
= message_table
[errnum
];
130 len
= ((error_string
== NULL
) ?
131 snprintf(buf
, buflen
, "Unknown error %d", errnum
) :
132 snprintf(buf
, buflen
, "%s", error_string
));
137 return "System error";
140 /* Convert an error code to a descriptive string. */
141 const char *uds_string_error(int errnum
, char *buf
, size_t buflen
)
144 char *buf_end
= buf
+ buflen
;
145 const struct error_info
*info
= NULL
;
146 const char *block_name
;
154 block_name
= get_error_info(errnum
, &info
);
155 if (block_name
!= NULL
) {
157 buffer
= vdo_append_to_buffer(buffer
, buf_end
, "%s: %s",
158 block_name
, info
->message
);
160 buffer
= vdo_append_to_buffer(buffer
, buf_end
, "Unknown %s %d",
163 } else if (info
!= NULL
) {
164 buffer
= vdo_append_to_buffer(buffer
, buf_end
, "%s", info
->message
);
166 const char *tmp
= system_string_error(errnum
, buffer
, buf_end
- buffer
);
169 buffer
= vdo_append_to_buffer(buffer
, buf_end
, "%s", tmp
);
171 buffer
+= strlen(tmp
);
177 /* Convert an error code to its name. */
178 const char *uds_string_error_name(int errnum
, char *buf
, size_t buflen
)
181 char *buf_end
= buf
+ buflen
;
182 const struct error_info
*info
= NULL
;
183 const char *block_name
;
188 block_name
= get_error_info(errnum
, &info
);
189 if (block_name
!= NULL
) {
191 buffer
= vdo_append_to_buffer(buffer
, buf_end
, "%s", info
->name
);
193 buffer
= vdo_append_to_buffer(buffer
, buf_end
, "%s %d",
196 } else if (info
!= NULL
) {
197 buffer
= vdo_append_to_buffer(buffer
, buf_end
, "%s", info
->name
);
201 tmp
= system_string_error(errnum
, buffer
, buf_end
- buffer
);
203 buffer
= vdo_append_to_buffer(buffer
, buf_end
, "%s", tmp
);
205 buffer
+= strlen(tmp
);
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.
216 int uds_status_to_errno(int error
)
218 char error_name
[VDO_MAX_ERROR_NAME_SIZE
];
219 char error_message
[VDO_MAX_ERROR_MESSAGE_SIZE
];
221 /* 0 is success, and negative values are already system error codes. */
222 if (likely(error
<= 0))
226 /* This is probably an errno from userspace. */
230 /* Internal UDS errors */
233 case UDS_CORRUPT_DATA
:
234 /* The index doesn't exist or can't be recovered. */
237 case UDS_INDEX_NOT_SAVED_CLEANLY
:
238 case UDS_UNSUPPORTED_VERSION
:
240 * The index exists, but can't be loaded. Tell the client it exists so they don't
241 * destroy it inadvertently.
246 /* The session is unusable; only returned by requests. */
250 /* Translate an unexpected error into something generic. */
251 vdo_log_info("%s: mapping status code %d (%s: %s) to -EIO",
253 uds_string_error_name(error
, error_name
,
255 uds_string_error(error
, error_message
,
256 sizeof(error_message
)));
262 * Register a block of error codes.
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
270 int uds_register_error_block(const char *block_name
, int first_error
,
271 int next_free_error
, const struct error_info
*infos
,
275 struct error_block
*block
;
276 struct error_block new_block
= {
279 .last
= first_error
+ (info_size
/ sizeof(struct error_info
)),
280 .max
= next_free_error
,
284 result
= VDO_ASSERT(first_error
< next_free_error
,
285 "well-defined error block range");
286 if (result
!= VDO_SUCCESS
)
289 if (registered_errors
.count
== registered_errors
.allocated
) {
290 /* This should never happen. */
294 for (block
= registered_errors
.blocks
;
295 block
< registered_errors
.blocks
+ registered_errors
.count
;
297 if (strcmp(block_name
, block
->name
) == 0)
298 return UDS_DUPLICATE_NAME
;
300 /* Ensure error ranges do not overlap. */
301 if ((first_error
< block
->max
) && (next_free_error
> block
->base
))
302 return UDS_ALREADY_REGISTERED
;
305 registered_errors
.blocks
[registered_errors
.count
++] = new_block
;