mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-07 06:20:28 +01:00
We have nm_strerror_native_r(), which is the wrapper around strerror_r() that we want to use in glib components (it also will ensure that the string is valid UTF-8). However, it's not usable from non-glib components. Move the part that abstracts strerror_r() out to libnm-std-aux as _nm_strerror_r(). The purpose is that non-glib componenent can use the thread-safe wrapper around strerror_r().
134 lines
4.2 KiB
C
134 lines
4.2 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include "nm-default-std.h"
|
|
|
|
#include "nm-std-utils.h"
|
|
|
|
#include <stdint.h>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
#include <net/if.h>
|
|
|
|
/*****************************************************************************/
|
|
|
|
NM_STATIC_ASSERT(NM_IFNAMSIZ == IFNAMSIZ);
|
|
|
|
/*****************************************************************************/
|
|
size_t
|
|
nm_utils_get_next_realloc_size(bool true_realloc, size_t requested)
|
|
{
|
|
size_t n, x;
|
|
|
|
/* https://doc.qt.io/qt-5/containers.html#growth-strategies */
|
|
|
|
if (requested <= 40) {
|
|
/* small allocations. Increase in small steps of 8 bytes.
|
|
*
|
|
* We get thus sizes of 8, 16, 32, 40. */
|
|
if (requested <= 8)
|
|
return 8;
|
|
if (requested <= 16)
|
|
return 16;
|
|
if (requested <= 32)
|
|
return 32;
|
|
|
|
/* The return values for < 104 are essentially hard-coded, and the choice here is
|
|
* made without very strong reasons.
|
|
*
|
|
* We want to stay 24 bytes below the power-of-two border 64. Hence, return 40 here.
|
|
* However, the next step then is already 104 (128 - 24). It's a larger gap than in
|
|
* the steps before.
|
|
*
|
|
* It's not clear whether some of the steps should be adjusted (or how exactly). */
|
|
return 40;
|
|
}
|
|
|
|
if (requested <= 0x2000u - 24u || NM_UNLIKELY(!true_realloc)) {
|
|
/* mid sized allocations. Return next power of two, minus 24 bytes extra space
|
|
* at the beginning.
|
|
* That means, we double the size as we grow.
|
|
*
|
|
* With !true_realloc, it means that the caller does not intend to call
|
|
* realloc() but instead clone the buffer. This is for example the case, when we
|
|
* want to nm_explicit_bzero() the old buffer. In that case we really want to grow
|
|
* the buffer exponentially every time and not increment in page sizes of 4K (below).
|
|
*
|
|
* We get thus sizes of 104, 232, 488, 1000, 2024, 4072, 8168... */
|
|
|
|
if (NM_UNLIKELY(requested > SIZE_MAX / 2u - 24u))
|
|
goto out_huge;
|
|
|
|
x = requested + 24u;
|
|
n = 128u;
|
|
while (n < x) {
|
|
n <<= 1;
|
|
nm_assert(n > 128u);
|
|
}
|
|
|
|
nm_assert(n > 24u && n - 24u >= requested);
|
|
return n - 24u;
|
|
}
|
|
|
|
if (NM_UNLIKELY(requested > SIZE_MAX - 0x1000u - 24u)) {
|
|
/* overflow happened. */
|
|
goto out_huge;
|
|
}
|
|
|
|
/* For large allocations (with !true_realloc) we allocate memory in chunks of
|
|
* 4K (- 24 bytes extra), assuming that the memory gets mmapped and thus
|
|
* realloc() is efficient by just reordering pages. */
|
|
n = ((requested + (0x0FFFu + 24u)) & ~((size_t) 0x0FFFu)) - 24u;
|
|
nm_assert(n >= requested);
|
|
return n;
|
|
|
|
out_huge:
|
|
if (sizeof(size_t) > 4u) {
|
|
/* on s390x (64 bit), gcc with LTO can complain that the size argument to
|
|
* malloc must not be larger than 9223372036854775807.
|
|
*
|
|
* Work around that by returning SSIZE_MAX. It should be plenty still! */
|
|
assert(requested <= (size_t) SSIZE_MAX);
|
|
return (size_t) SSIZE_MAX;
|
|
}
|
|
return SIZE_MAX;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* _nm_strerror_r:
|
|
* @errsv: the errno passed to strerror_r()
|
|
* @buf: the string buffer, must be non-null
|
|
* @buf_size: the size of the buffer, must be positive.
|
|
*
|
|
* A wrapper around strerror_r(). Does little else, aside clearing up the
|
|
* confusion about the different versions of the function.
|
|
*
|
|
* errno is preserved.
|
|
*
|
|
* Returns: the error string. This is either a static strong or @buf. It
|
|
* is not guaranteed to be @buf.
|
|
*/
|
|
const char *
|
|
_nm_strerror_r(int errsv, char *buf, size_t buf_size)
|
|
{
|
|
NM_AUTO_PROTECT_ERRNO(errsv2);
|
|
char *buf2;
|
|
|
|
nm_assert(buf);
|
|
nm_assert(buf_size > 0);
|
|
|
|
#if (!defined(__GLIBC__) && !defined(__UCLIBC__)) || ((_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE)
|
|
/* XSI-compliant */
|
|
if (strerror_r(errsv, buf, buf_size) != 0) {
|
|
snprintf(buf, buf_size, "Unspecified errno %d", errsv);
|
|
}
|
|
buf2 = buf;
|
|
#else
|
|
/* GNU-specific */
|
|
buf2 = strerror_r(errsv, buf, buf_size);
|
|
#endif
|
|
|
|
nm_assert(buf2);
|
|
return buf2;
|
|
}
|