From 1c391d734c2ed554dca2b66358ddeceb60149663 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 5 Mar 2026 13:20:41 +0000 Subject: [PATCH] backports, local-alloc: Provide a backport of g_autofd The functionality that was prototyped in libglnx as glnx_fd_close and then glnx_autofd was later added to GLib as g_autofd. glnx_close_fd() doesn't have a direct equivalent in GLib, so keep it intact, but using the backported _glnx_clear_fd_ignore_error as its implementation. g_clear_fd() is the closest thing in GLib, but g_clear_fd() guarantees to set errno on failure (making it useful for error-checking), whereas glnx_close_fd() guarantees to leave errno untouched, making it more useful for cleanup code paths that recover from a syscall or similar function that sets errno: int fd = ...; success = (fsync (fd) == 0); glnx_close_fd (&fd); return success; /* if false, errno indicates why fsync failed */ (Of course in many cases, including this simple example, it would have been easier to use g_autofd.) glnx_autofd and glnx_fd_close are now equivalent to the backport of g_autofd, so document them as deprecated. There's essentially no cost to retaining them, so don't apply deprecation attributes. Signed-off-by: Simon McVittie --- glnx-backports.h | 21 +++++++++++++++++++++ glnx-local-alloc.h | 31 ++++++++----------------------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/glnx-backports.h b/glnx-backports.h index 9ec11e4cf..a0177ad99 100644 --- a/glnx-backports.h +++ b/glnx-backports.h @@ -82,6 +82,27 @@ g_clear_fd (int *fd_ptr, } #endif +/* This is part of the backport of g_autofd, but we define it + * unconditionally because it's also used to implement glnx_close_fd() */ +static inline void +_glnx_clear_fd_ignore_error (int *fd_ptr) +{ + /* Don't overwrite thread-local errno if closing the fd fails */ + int errsv = errno; + + if (!g_clear_fd (fd_ptr, NULL)) + { + /* Do nothing: we ignore all errors, except for EBADF which + * is a programming error, checked for by g_close(). */ + } + + errno = errsv; +} + +#if !GLIB_CHECK_VERSION(2, 76, 0) +#define g_autofd __attribute__((cleanup(_glnx_clear_fd_ignore_error))) +#endif + #if !GLIB_CHECK_VERSION(2, 40, 0) #define g_info(...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__) #endif diff --git a/glnx-local-alloc.h b/glnx-local-alloc.h index a6e0e9b4f..9ac8a027d 100644 --- a/glnx-local-alloc.h +++ b/glnx-local-alloc.h @@ -51,38 +51,23 @@ glnx_local_obj_unref (void *v) * glnx_close_fd: * @fdp: Pointer to fd * - * Effectively `close (g_steal_fd (&fd))`. Also - * asserts that `close()` did not raise `EBADF` - encountering - * that error is usually a critical bug in the program. + * Same as `g_clear_fd()`, but ignoring the error (if any) and making sure + * not to alter `errno`. As a result, this function can be used for cleanup + * in contexts where `errno` needs to be preserved. */ -static inline void -glnx_close_fd (int *fdp) -{ - int errsv; - - g_assert (fdp); - - int fd = g_steal_fd (fdp); - if (fd >= 0) - { - errsv = errno; - if (close (fd) < 0) - g_assert (errno != EBADF); - errno = errsv; - } -} +#define glnx_close_fd _glnx_clear_fd_ignore_error /** * glnx_fd_close: * - * Deprecated in favor of `glnx_autofd`. + * Deprecated in favor of `g_autofd`. */ -#define glnx_fd_close __attribute__((cleanup(glnx_close_fd))) +#define glnx_fd_close g_autofd /** * glnx_autofd: * - * Call close() on a variable location when it goes out of scope. + * Deprecated in favor of `g_autofd`. */ -#define glnx_autofd __attribute__((cleanup(glnx_close_fd))) +#define glnx_autofd g_autofd G_END_DECLS