From 627d4e2f15572e853279d4bb9511ef2618bef6b5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 6 Sep 2017 14:06:37 -0400 Subject: [PATCH] fdio: Add glnx_fstatat_allow_noent() This is a very common pattern in both ostree/rpm-ostree. Make a better API for this. I thought a lot about simply zeroing out `struct stat` but that feels dangerous; none of the values have seem obviously `cannot be zero`. --- glnx-fdio.h | 38 ++++++++++++++++++++++++++++++++++++++ tests/test-libglnx-fdio.c | 22 ++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/glnx-fdio.h b/glnx-fdio.h index 4ee24afa..518135c5 100644 --- a/glnx-fdio.h +++ b/glnx-fdio.h @@ -295,6 +295,44 @@ glnx_fstatat (int dfd, return TRUE; } +/** + * glnx_fstatat_allow_noent: + * @dfd: Directory FD to stat beneath + * @path: Path to stat beneath @dfd + * @buf: (out caller-allocates): Return location for stat details + * @flags: Flags to pass to fstatat() + * @error: Return location for a #GError, or %NULL + * + * Like glnx_fstatat(), but handles `ENOENT` in a non-error way. Instead, + * on success `errno` will be zero, otherwise it will be preserved. Hence + * you can test `if (errno == 0)` to conditionalize on the file existing, + * or `if (errno == ENOENT)` for non-existence. + * + * Returns: %TRUE on success, %FALSE otherwise (errno is preserved) + * Since: UNRELEASED + */ +static inline gboolean +glnx_fstatat_allow_noent (int dfd, + const char *path, + struct stat *out_buf, + int flags, + GError **error) +{ + if (TEMP_FAILURE_RETRY (fstatat (dfd, path, out_buf, flags)) != 0) + { + if (errno != ENOENT) + { + int errsv = errno; + (void) glnx_throw_errno_prefix (error, "fstatat(%s)", path); + errno = errsv; + return FALSE; + } + } + else + errno = 0; + return TRUE; +} + /** * glnx_renameat: * diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 16b66926..4b81a957 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -189,6 +189,27 @@ test_stdio_file (void) g_assert_no_error (local_error); } +static void +test_fstatat (void) +{ + g_autoptr(GError) local_error = NULL; + GError **error = &local_error; + struct stat stbuf = { 0, }; + + if (!glnx_fstatat_allow_noent (AT_FDCWD, ".", &stbuf, 0, error)) + goto out; + g_assert_cmpint (errno, ==, 0); + g_assert_no_error (local_error); + g_assert (S_ISDIR (stbuf.st_mode)); + if (!glnx_fstatat_allow_noent (AT_FDCWD, "nosuchfile", &stbuf, 0, error)) + goto out; + g_assert_cmpint (errno, ==, ENOENT); + g_assert_no_error (local_error); + + out: + g_assert_no_error (local_error); +} + int main (int argc, char **argv) { int ret; @@ -199,6 +220,7 @@ int main (int argc, char **argv) g_test_add_func ("/stdio-file", test_stdio_file); g_test_add_func ("/renameat2-noreplace", test_renameat2_noreplace); g_test_add_func ("/renameat2-exchange", test_renameat2_exchange); + g_test_add_func ("/fstat", test_fstatat); ret = g_test_run();