basic/errno-util: add wrappers which only accept negative errno

We do 'IN_SET(r, -CONST1, -CONST2)', instead of 'IN_SET(-r, CONST1, CONST2)'
because -r is undefined if r is the minimum value (i.e. INT_MIN). But we know
that the constants are small, so their negative values are fine.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2023-08-09 16:36:38 +02:00
parent aea0f9abc5
commit b0be985cdd
2 changed files with 80 additions and 56 deletions

View file

@ -94,12 +94,21 @@ static inline int errno_or_else(int fallback) {
return -abs(fallback);
}
/* abs(3) says: Trying to take the absolute value of the most negative integer is not defined. */
#define _DEFINE_ABS_WRAPPER(name) \
static inline bool ERRNO_IS_##name(int r) { \
if (r == INT_MIN) \
return false; \
return ERRNO_IS_NEG_##name(-abs(r)); \
}
/* For send()/recv() or read()/write(). */
static inline bool ERRNO_IS_TRANSIENT(int r) {
return IN_SET(abs(r),
EAGAIN,
EINTR);
static inline bool ERRNO_IS_NEG_TRANSIENT(int r) {
return IN_SET(r,
-EAGAIN,
-EINTR);
}
_DEFINE_ABS_WRAPPER(TRANSIENT);
/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
*
@ -108,79 +117,87 @@ static inline bool ERRNO_IS_TRANSIENT(int r) {
*
* Hint #3: When asynchronous connect() on TCP fails because the host never acknowledges a single packet,
* kernel tells us that with ETIMEDOUT, see tcp(7). */
static inline bool ERRNO_IS_DISCONNECT(int r) {
return IN_SET(abs(r),
ECONNABORTED,
ECONNREFUSED,
ECONNRESET,
EHOSTDOWN,
EHOSTUNREACH,
ENETDOWN,
ENETRESET,
ENETUNREACH,
ENONET,
ENOPROTOOPT,
ENOTCONN,
EPIPE,
EPROTO,
ESHUTDOWN,
ETIMEDOUT);
static inline bool ERRNO_IS_NEG_DISCONNECT(int r) {
return IN_SET(r,
-ECONNABORTED,
-ECONNREFUSED,
-ECONNRESET,
-EHOSTDOWN,
-EHOSTUNREACH,
-ENETDOWN,
-ENETRESET,
-ENETUNREACH,
-ENONET,
-ENOPROTOOPT,
-ENOTCONN,
-EPIPE,
-EPROTO,
-ESHUTDOWN,
-ETIMEDOUT);
}
_DEFINE_ABS_WRAPPER(DISCONNECT);
/* Transient errors we might get on accept() that we should ignore. As per error handling comment in
* the accept(2) man page. */
static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) {
return ERRNO_IS_DISCONNECT(r) ||
ERRNO_IS_TRANSIENT(r) ||
abs(r) == EOPNOTSUPP;
static inline bool ERRNO_IS_NEG_ACCEPT_AGAIN(int r) {
return ERRNO_IS_NEG_DISCONNECT(r) ||
ERRNO_IS_NEG_TRANSIENT(r) ||
r == -EOPNOTSUPP;
}
_DEFINE_ABS_WRAPPER(ACCEPT_AGAIN);
/* Resource exhaustion, could be our fault or general system trouble */
static inline bool ERRNO_IS_RESOURCE(int r) {
return IN_SET(abs(r),
EMFILE,
ENFILE,
ENOMEM);
static inline bool ERRNO_IS_NEG_RESOURCE(int r) {
return IN_SET(r,
-EMFILE,
-ENFILE,
-ENOMEM);
}
_DEFINE_ABS_WRAPPER(RESOURCE);
/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */
static inline bool ERRNO_IS_NOT_SUPPORTED(int r) {
return IN_SET(abs(r),
EOPNOTSUPP,
ENOTTY,
ENOSYS,
EAFNOSUPPORT,
EPFNOSUPPORT,
EPROTONOSUPPORT,
ESOCKTNOSUPPORT);
static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(int r) {
return IN_SET(r,
-EOPNOTSUPP,
-ENOTTY,
-ENOSYS,
-EAFNOSUPPORT,
-EPFNOSUPPORT,
-EPROTONOSUPPORT,
-ESOCKTNOSUPPORT);
}
_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
/* Two different errors for access problems */
static inline bool ERRNO_IS_PRIVILEGE(int r) {
return IN_SET(abs(r),
EACCES,
EPERM);
static inline bool ERRNO_IS_NEG_PRIVILEGE(int r) {
return IN_SET(r,
-EACCES,
-EPERM);
}
_DEFINE_ABS_WRAPPER(PRIVILEGE);
/* Three different errors for "not enough disk space" */
static inline bool ERRNO_IS_DISK_SPACE(int r) {
return IN_SET(abs(r),
ENOSPC,
EDQUOT,
EFBIG);
static inline bool ERRNO_IS_NEG_DISK_SPACE(int r) {
return IN_SET(r,
-ENOSPC,
-EDQUOT,
-EFBIG);
}
_DEFINE_ABS_WRAPPER(DISK_SPACE);
/* Three different errors for "this device does not quite exist" */
static inline bool ERRNO_IS_DEVICE_ABSENT(int r) {
return IN_SET(abs(r),
ENODEV,
ENXIO,
ENOENT);
static inline bool ERRNO_IS_NEG_DEVICE_ABSENT(int r) {
return IN_SET(r,
-ENODEV,
-ENXIO,
-ENOENT);
}
_DEFINE_ABS_WRAPPER(DEVICE_ABSENT);
/* Quite often we want to handle cases where the backing FS doesn't support extended attributes at all and
* where it simply doesn't have the requested xattr the same way */
static inline bool ERRNO_IS_XATTR_ABSENT(int r) {
return abs(r) == ENODATA ||
ERRNO_IS_NOT_SUPPORTED(r);
static inline bool ERRNO_IS_NEG_XATTR_ABSENT(int r) {
return r == -ENODATA ||
ERRNO_IS_NEG_NOT_SUPPORTED(r);
}
_DEFINE_ABS_WRAPPER(XATTR_ABSENT);

View file

@ -89,4 +89,11 @@ TEST(RET_GATHER) {
assert_se(y == 3);
}
TEST(ERRNO_IS_TRANSIENT) {
assert_se( ERRNO_IS_NEG_TRANSIENT(-EINTR));
assert_se(!ERRNO_IS_NEG_TRANSIENT(EINTR));
assert_se( ERRNO_IS_TRANSIENT(-EINTR));
assert_se( ERRNO_IS_TRANSIENT(EINTR));
}
DEFINE_TEST_MAIN(LOG_INFO);