diff --git a/pressure-vessel/utils.c b/pressure-vessel/utils.c index d82cb81380f406b63b4e3dbb20c41d6f857c9b16..7834bcd79d3a84e99f47c472cd7f3098b60e15ed 100644 --- a/pressure-vessel/utils.c +++ b/pressure-vessel/utils.c @@ -37,6 +37,7 @@ #include "steam-runtime-tools/glib-backports-internal.h" #include "steam-runtime-tools/resolve-in-sysroot-internal.h" +#include "steam-runtime-tools/utils-internal.h" #include "flatpak-bwrap-private.h" #include "flatpak-utils-base-private.h" #include "flatpak-utils-private.h" @@ -148,38 +149,13 @@ pv_get_current_dirs (gchar **cwd_p, { pwd = g_getenv ("PWD"); - if (pwd != NULL && pv_is_same_file (pwd, cwd)) + if (pwd != NULL && _srt_is_same_file (pwd, cwd)) *cwd_l = g_strdup (pwd); else *cwd_l = g_strdup (cwd); } } -/** - * pv_is_same_file: - * @a: a path - * @b: a path - * - * Returns: %TRUE if a and b are names for the same inode. - */ -gboolean -pv_is_same_file (const gchar *a, - const gchar *b) -{ - GStatBuf a_buffer, b_buffer; - - g_return_val_if_fail (a != NULL, FALSE); - g_return_val_if_fail (b != NULL, FALSE); - - if (strcmp (a, b) == 0) - return TRUE; - - return (stat (a, &a_buffer) == 0 - && stat (b, &b_buffer) == 0 - && a_buffer.st_dev == b_buffer.st_dev - && a_buffer.st_ino == b_buffer.st_ino); -} - void pv_search_path_append (GString *search_path, const gchar *item) diff --git a/pressure-vessel/utils.h b/pressure-vessel/utils.h index 109595344136d04c9aa16eed461c8fe4d8f01d7c..46fa32ecfccaf62cff4329ee54111e94a4c976b4 100644 --- a/pressure-vessel/utils.h +++ b/pressure-vessel/utils.h @@ -45,9 +45,6 @@ int pv_envp_cmp (const void *p1, void pv_get_current_dirs (gchar **cwd_p, gchar **cwd_l); -gboolean pv_is_same_file (const gchar *a, - const gchar *b); - void pv_search_path_append (GString *search_path, const gchar *item); diff --git a/pressure-vessel/wrap.c b/pressure-vessel/wrap.c index e9b3aa26ae54783a266617be1f38bc96aa18a40f..1c727efc60205368b3b0a5e82d3a0c3500a72b3a 100644 --- a/pressure-vessel/wrap.c +++ b/pressure-vessel/wrap.c @@ -1973,7 +1973,7 @@ main (int argc, cwd_p_host = pv_current_namespace_path_to_host_path (cwd_p); - if (pv_is_same_file (home, cwd_p)) + if (_srt_is_same_file (home, cwd_p)) { g_debug ("Not making physical working directory \"%s\" available to " "container because it is the home directory", diff --git a/steam-runtime-tools/utils-internal.h b/steam-runtime-tools/utils-internal.h index 7e06650ce58a47fbef63880cb3268263afc1df48..6fdf671d7a8d15b51efbdfa214e55f38d1e45311 100644 --- a/steam-runtime-tools/utils-internal.h +++ b/steam-runtime-tools/utils-internal.h @@ -26,7 +26,9 @@ #pragma once +#include <fcntl.h> #include <stdio.h> +#include <sys/stat.h> #include <glib.h> @@ -103,3 +105,35 @@ G_GNUC_INTERNAL const char * const *_srt_peek_environ_nonnull (void); G_GNUC_INTERNAL void _srt_setenv_disable_gio_modules (void); G_GNUC_INTERNAL gboolean _srt_str_is_integer (const char *str); + +gboolean _srt_fstatat_is_same_file (int afd, const char *a, + int bfd, const char *b); + +/* + * _srt_is_same_stat: + * @a: a stat buffer + * @b: a stat buffer + * + * Returns: %TRUE if a and b identify the same inode + */ + __attribute__((nonnull)) static inline gboolean +_srt_is_same_stat (const struct stat *a, + const struct stat *b) +{ + return (a->st_dev == b->st_dev && a->st_ino == b->st_ino); +} + +/* + * _srt_is_same_file: + * @a: a path + * @b: a path + * + * Returns: %TRUE if a and b are names for the same inode. + */ +static inline gboolean +_srt_is_same_file (const gchar *a, + const gchar *b) +{ + return _srt_fstatat_is_same_file (AT_FDCWD, a, + AT_FDCWD, b); +} diff --git a/steam-runtime-tools/utils.c b/steam-runtime-tools/utils.c index 5601a1778b291ea0d469f3629d41ba793e031c47..8aa4935433d684161b67e817c78a2ce310dfc3c3 100644 --- a/steam-runtime-tools/utils.c +++ b/steam-runtime-tools/utils.c @@ -986,3 +986,35 @@ _srt_str_is_integer (const char *str) return (*p == '\0'); } + +/* + * _srt_fstatat_is_same_file: + * @afd: a file descriptor, `AT_FDCWD` or -1 + * @a: a path + * @bfd: a file descriptor, `AT_FDCWD` or -1 + * @b: a path + * + * Returns: %TRUE if a (relative to afd) and b (relative to bfd) + * are names for the same inode. + */ +gboolean +_srt_fstatat_is_same_file (int afd, + const char *a, + int bfd, + const char *b) +{ + struct stat a_buffer, b_buffer; + + g_return_val_if_fail (a != NULL, FALSE); + g_return_val_if_fail (b != NULL, FALSE); + + afd = glnx_dirfd_canonicalize (afd); + bfd = glnx_dirfd_canonicalize (bfd); + + if (afd == bfd && strcmp (a, b) == 0) + return TRUE; + + return (fstatat (afd, a, &a_buffer, AT_EMPTY_PATH) == 0 + && fstatat (bfd, b, &b_buffer, AT_EMPTY_PATH) == 0 + && _srt_is_same_stat (&a_buffer, &b_buffer)); +} diff --git a/tests/graphics.c b/tests/graphics.c index 58fd4823828d538b5f5252f44f8f6371774617db..b8e527bb4daedf9a844630540fabfdf3be37dcc6 100644 --- a/tests/graphics.c +++ b/tests/graphics.c @@ -41,6 +41,7 @@ #include "steam-runtime-tools/graphics-internal.h" #include "steam-runtime-tools/graphics-test-defines.h" #include "steam-runtime-tools/system-info.h" +#include "steam-runtime-tools/utils-internal.h" #include "test-utils.h" static const char *argv0; @@ -528,13 +529,6 @@ assert_egl_icd_no_error (SrtEglIcd *icd) assert_egl_icd (icd); } -static gboolean -same_stat (GStatBuf *left, - GStatBuf *right) -{ - return left->st_dev == right->st_dev && left->st_ino == right->st_ino; -} - /* * We don't assert that filenames are literally the same, because they * might canonicalize differently in the presence of symlinks: we just @@ -544,15 +538,15 @@ static void assert_same_file (const char *expected, const char *actual) { - GStatBuf expected_stat, actual_stat; + struct stat expected_stat, actual_stat; - if (g_stat (expected, &expected_stat) != 0) + if (stat (expected, &expected_stat) != 0) g_error ("stat %s: %s", expected, g_strerror (errno)); - if (g_stat (actual, &actual_stat) != 0) + if (stat (actual, &actual_stat) != 0) g_error ("stat %s: %s", actual, g_strerror (errno)); - if (!same_stat (&expected_stat, &actual_stat)) + if (!_srt_is_same_stat (&expected_stat, &actual_stat)) g_error ("%s is not the same file as %s", expected, actual); } diff --git a/tests/pressure-vessel/utils.c b/tests/pressure-vessel/utils.c index d900b89643b799d51e435957434848dca84b0296..8c123b760bbfd1c0b7536b1ab01029d7b29dc4dd 100644 --- a/tests/pressure-vessel/utils.c +++ b/tests/pressure-vessel/utils.c @@ -182,52 +182,6 @@ test_envp_cmp (Fixture *f, g_free (sort_this); } -static void -test_same_file (Fixture *f, - gconstpointer context) -{ - g_autoptr(GError) error = NULL; - g_autofree gchar *temp = NULL; - g_autofree gchar *hard_link_from = NULL; - g_autofree gchar *hard_link_to = NULL; - g_autofree gchar *symlink_to_dev_null = NULL; - - g_assert_true (pv_is_same_file ("/dev/null", "/dev/null")); - g_assert_true (pv_is_same_file ("/nonexistent", "/nonexistent")); - g_assert_false (pv_is_same_file ("/dev/null", "/dev/zero")); - g_assert_false (pv_is_same_file ("/dev/null", "/nonexistent")); - g_assert_false (pv_is_same_file ("/nonexistent", "/dev/null")); - g_assert_false (pv_is_same_file ("/nonexistent", "/nonexistent/also")); - - temp = g_dir_make_tmp (NULL, &error); - g_assert_no_error (error); - g_assert_nonnull (temp); - - hard_link_from = g_build_filename (temp, "hard-link-from", NULL); - hard_link_to = g_build_filename (temp, "hard-link-to", NULL); - symlink_to_dev_null = g_build_filename (temp, "symlink", NULL); - - g_file_set_contents (hard_link_from, "hello", -1, NULL); - g_assert_no_error (error); - - if (link (hard_link_from, hard_link_to) != 0) - g_error ("Could not create hard link \"%s\" -> /dev/null: %s", - symlink_to_dev_null, g_strerror (errno)); - - g_assert_true (pv_is_same_file (hard_link_from, hard_link_to)); - g_assert_false (pv_is_same_file (hard_link_from, "/dev/null")); - - if (symlink ("/dev/null", symlink_to_dev_null) != 0) - g_error ("Could not create symlink \"%s\" -> /dev/null: %s", - symlink_to_dev_null, g_strerror (errno)); - - g_assert_true (pv_is_same_file (symlink_to_dev_null, "/dev/null")); - g_assert_false (pv_is_same_file (symlink_to_dev_null, "/dev/zero")); - - glnx_shutil_rm_rf_at (-1, temp, NULL, &error); - g_assert_no_error (error); -} - static void test_search_path_append (Fixture *f, gconstpointer context) @@ -269,7 +223,6 @@ main (int argc, g_test_add ("/capture-output", Fixture, NULL, setup, test_capture_output, teardown); g_test_add ("/envp-cmp", Fixture, NULL, setup, test_envp_cmp, teardown); - g_test_add ("/same-file", Fixture, NULL, setup, test_same_file, teardown); g_test_add ("/search-path-append", Fixture, NULL, setup, test_search_path_append, teardown); diff --git a/tests/utils.c b/tests/utils.c index b7bb9102c94256d5de7bc36c382bbe9e8f7eee2f..312913182662f1900d3c155cddb6754ed1417226 100644 --- a/tests/utils.c +++ b/tests/utils.c @@ -299,6 +299,52 @@ filter_gameoverlayrenderer (Fixture *f, g_free (filtered_preload); } +static void +test_same_file (Fixture *f, + gconstpointer context) +{ + g_autoptr(GError) error = NULL; + g_autofree gchar *temp = NULL; + g_autofree gchar *hard_link_from = NULL; + g_autofree gchar *hard_link_to = NULL; + g_autofree gchar *symlink_to_dev_null = NULL; + + g_assert_true (_srt_is_same_file ("/dev/null", "/dev/null")); + g_assert_true (_srt_is_same_file ("/nonexistent", "/nonexistent")); + g_assert_false (_srt_is_same_file ("/dev/null", "/dev/zero")); + g_assert_false (_srt_is_same_file ("/dev/null", "/nonexistent")); + g_assert_false (_srt_is_same_file ("/nonexistent", "/dev/null")); + g_assert_false (_srt_is_same_file ("/nonexistent", "/nonexistent/also")); + + temp = g_dir_make_tmp (NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (temp); + + hard_link_from = g_build_filename (temp, "hard-link-from", NULL); + hard_link_to = g_build_filename (temp, "hard-link-to", NULL); + symlink_to_dev_null = g_build_filename (temp, "symlink", NULL); + + g_file_set_contents (hard_link_from, "hello", -1, NULL); + g_assert_no_error (error); + + if (link (hard_link_from, hard_link_to) != 0) + g_error ("Could not create hard link \"%s\" -> /dev/null: %s", + symlink_to_dev_null, g_strerror (errno)); + + g_assert_true (_srt_is_same_file (hard_link_from, hard_link_to)); + g_assert_false (_srt_is_same_file (hard_link_from, "/dev/null")); + + if (symlink ("/dev/null", symlink_to_dev_null) != 0) + g_error ("Could not create symlink \"%s\" -> /dev/null: %s", + symlink_to_dev_null, g_strerror (errno)); + + g_assert_true (_srt_is_same_file (symlink_to_dev_null, "/dev/null")); + g_assert_false (_srt_is_same_file (symlink_to_dev_null, "/dev/zero")); + + glnx_shutil_rm_rf_at (-1, temp, NULL, &error); + g_assert_no_error (error); +} + static void test_str_is_integer (Fixture *f, gconstpointer context) @@ -384,6 +430,8 @@ main (int argc, setup, test_file_in_sysroot, teardown); g_test_add ("/utils/filter_gameoverlayrenderer", Fixture, NULL, setup, filter_gameoverlayrenderer, teardown); + g_test_add ("/utils/same-file", Fixture, NULL, + setup, test_same_file, teardown); g_test_add ("/utils/str_is_integer", Fixture, NULL, setup, test_str_is_integer, teardown); g_test_add ("/utils/uevent-field", Fixture, NULL,