From f92968a8d253cb6581ba80d67fc863f1aa85eef7 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 5 Dec 2024 11:35:45 +0000 Subject: [PATCH 1/4] build: Use a boolean default for a boolean option, rather than a string Meson 1.1.0 officially deprecates string defaults for boolean options, but boolean defaults worked in many older Meson versions, going back to at least 0.49.x. Signed-off-by: Simon McVittie --- meson_options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson_options.txt b/meson_options.txt index 1028017f..e9001114 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -5,5 +5,5 @@ option( 'tests', type : 'boolean', description : 'build and run unit tests', - value : 'true', + value : true, ) From f44d9442330c5ff35fc02c1097d2c0eb3af7d3c8 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 5 Dec 2024 11:48:18 +0000 Subject: [PATCH 2/4] tests: Run each shutil test in a temporary directory Otherwise it could potentially race with tests in other executables that also want to create `./test`, or interfere with other test-cases in the same executable that expect `./test` to be nonexistent or empty. Signed-off-by: Simon McVittie --- tests/test-libglnx-shutil.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test-libglnx-shutil.c b/tests/test-libglnx-shutil.c index 28b34abe..42c3f109 100644 --- a/tests/test-libglnx-shutil.c +++ b/tests/test-libglnx-shutil.c @@ -32,6 +32,7 @@ static void test_mkdir_p_enoent (void) { + _GLNX_TEST_SCOPED_TEMP_DIR; _GLNX_TEST_DECLARE_ERROR(local_error, error); glnx_autofd int dfd = -1; From 2eb4bcc28202d66eac0978d46abfdec1d3c15fe6 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 5 Dec 2024 12:05:59 +0000 Subject: [PATCH 3/4] glnx-shutil: Cope with ENOENT even after recursing to create parents If we try to create `link/content` where `link` is a dangling symlink, recursing to create `link` will succeed (mkdirat fails with EEXIST, which is explicitly ignored), but then mkdirat for `link/content` will still fail. Fail gracefully instead of crashing out with an assertion failure. Resolves: GNOME/libglnx#1 Signed-off-by: Simon McVittie --- glnx-shutil.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/glnx-shutil.c b/glnx-shutil.c index 5ebe7f88..5a6fd7ce 100644 --- a/glnx-shutil.c +++ b/glnx-shutil.c @@ -149,12 +149,10 @@ mkdir_p_at_internal (int dfd, again: if (mkdirat (dfd, path, mode) == -1) { - if (errno == ENOENT) + if (errno == ENOENT && !did_recurse) { char *lastslash; - g_assert (!did_recurse); - lastslash = strrchr (path, '/'); if (lastslash == NULL) { From e6151ffbc0c42736a0d506283e1cb6844a5f54ef Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 5 Dec 2024 11:50:05 +0000 Subject: [PATCH 4/4] Add a test for glnx_shutil_mkdir_p_at with an unusable parent This is a slight generalization of the reproducer contributed by Will Thompson: as well as exercising the case where the parent is a dangling symlink (reproducing GNOME/libglnx#1), this also exercises the case where the parent is a non-directory non-symlink (in this case a regular file). Reproduces: GNOME/libglnx#1 Co-authored-by: Will Thompson Signed-off-by: Simon McVittie --- tests/test-libglnx-shutil.c | 41 ++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/test-libglnx-shutil.c b/tests/test-libglnx-shutil.c index 42c3f109..98e424aa 100644 --- a/tests/test-libglnx-shutil.c +++ b/tests/test-libglnx-shutil.c @@ -1,6 +1,7 @@ /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * - * Copyright © 2017 Endless OS Foundation LLC + * Copyright © 2017-2019 Endless OS Foundation LLC + * Copyright © 2024 Collabora Ltd. * SPDX-License-Identifier: LGPL-2.0-or-later * * This library is free software; you can redistribute it and/or @@ -29,6 +30,43 @@ #include "libglnx-testlib.h" +static void +test_mkdir_p_parent_unsuitable (void) +{ + _GLNX_TEST_SCOPED_TEMP_DIR; + _GLNX_TEST_DECLARE_ERROR(local_error, error); + glnx_autofd int dfd = -1; + + if (!glnx_ensure_dir (AT_FDCWD, "test", 0755, error)) + return; + if (!glnx_opendirat (AT_FDCWD, "test", FALSE, &dfd, error)) + return; + + if (!glnx_file_replace_contents_at (dfd, "file", + (const guint8 *) "", 0, + GLNX_FILE_REPLACE_NODATASYNC, + NULL, error)) + return; + + if (symlinkat ("nosuchtarget", dfd, "link") < 0) + { + glnx_throw_errno_prefix (error, "symlinkat"); + return; + } + + glnx_shutil_mkdir_p_at (dfd, "file/baz", 0755, NULL, error); + g_test_message ("mkdir %s -> %s", "file/baz", + local_error ? local_error->message : "success"); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY); + g_clear_error (&local_error); + + glnx_shutil_mkdir_p_at (dfd, "link/baz", 0755, NULL, error); + g_test_message ("mkdir %s -> %s", "link/baz", + local_error ? local_error->message : "success"); + g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + g_clear_error (&local_error); +} + static void test_mkdir_p_enoent (void) { @@ -58,6 +96,7 @@ main (int argc, g_test_init (&argc, &argv, NULL); g_test_add_func ("/mkdir-p/enoent", test_mkdir_p_enoent); + g_test_add_func ("/mkdir-p/parent-unsuitable", test_mkdir_p_parent_unsuitable); ret = g_test_run();