From 6f6986c333ed9d56c28e5ff419b33fb4eb158bc2 Mon Sep 17 00:00:00 2001 From: Matthias Klumpp Date: Wed, 5 Jun 2024 21:44:01 +0200 Subject: [PATCH] compose: Allow file discovery even in symlinked directories This was discovered to be an issue on Flathub with some component localizations being placed in symlinked directories. CC: https://github.com/flathub/flathub/issues/5272 --- compose/asc-directory-unit.c | 69 +++++++++++++++---- .../compose/usr/share/locale/dir_loop/loop | 1 + ...g.example.nonexistent-badlink.metainfo.xml | 1 + tests/test-compose.c | 2 +- tests/test-xmldata.c | 13 ++-- 5 files changed, 65 insertions(+), 21 deletions(-) create mode 120000 tests/samples/compose/usr/share/locale/dir_loop/loop create mode 120000 tests/samples/compose/usr/share/metainfo/org.example.nonexistent-badlink.metainfo.xml diff --git a/compose/asc-directory-unit.c b/compose/asc-directory-unit.c index 80698e873..10c9c3de1 100644 --- a/compose/asc-directory-unit.c +++ b/compose/asc-directory-unit.c @@ -81,11 +81,12 @@ asc_directory_unit_class_init (AscDirectoryUnitClass *klass) } static gboolean -asc_directory_unit_find_files_recursive (GPtrArray *files, - const gchar *path_orig, - guint path_orig_len, - const gchar *path, - GError **error) +asc_directory_unit_find_files_recursive_internal (GPtrArray *files, + const gchar *path_orig, + guint path_orig_len, + const gchar *path, + GHashTable *visited_dirs, + GError **error) { const gchar *tmp; g_autoptr(GDir) dir = NULL; @@ -104,14 +105,38 @@ asc_directory_unit_find_files_recursive (GPtrArray *files, g_autofree gchar *path_new = NULL; path_new = g_build_filename (path, tmp, NULL); - /* search recursively, don't follow symlinks */ - if (g_file_test (path_new, G_FILE_TEST_IS_DIR) && - !g_file_test (path_new, G_FILE_TEST_IS_SYMLINK)) { - if (!asc_directory_unit_find_files_recursive (files, - path_orig, - path_orig_len, - path_new, - error)) + /* search recursively */ + if (g_file_test (path_new, G_FILE_TEST_IS_DIR)) { + if (g_file_test (path_new, G_FILE_TEST_IS_SYMLINK)) { + g_autofree gchar *real_path = realpath (path_new, NULL); + + if (!real_path) { + /* error if realpath fails (like memory allocation error or invalid path) */ + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Failed to resolve real path"); + return FALSE; + } + + /* don't visit paths twice to avoid loops */ + if (g_hash_table_contains (visited_dirs, real_path)) + return TRUE; + + g_hash_table_add (visited_dirs, g_steal_pointer (&real_path)); + } else { + if (g_hash_table_contains (visited_dirs, path_new)) + return TRUE; + + g_hash_table_add (visited_dirs, g_strdup (path_new)); + } + + if (!asc_directory_unit_find_files_recursive_internal (files, + path_orig, + path_orig_len, + path_new, + visited_dirs, + error)) return FALSE; } else { g_ptr_array_add (files, g_strdup (path_new + path_orig_len)); @@ -121,6 +146,24 @@ asc_directory_unit_find_files_recursive (GPtrArray *files, return TRUE; } +static gboolean +asc_directory_unit_find_files_recursive (GPtrArray *files, + const gchar *path_orig, + guint path_orig_len, + const gchar *path, + GError **error) +{ + g_autoptr(GHashTable) visited_dirs = NULL; + visited_dirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + return asc_directory_unit_find_files_recursive_internal (files, + path_orig, + path_orig_len, + path, + visited_dirs, + error); +} + static gboolean asc_directory_unit_open (AscUnit *unit, GError **error) { diff --git a/tests/samples/compose/usr/share/locale/dir_loop/loop b/tests/samples/compose/usr/share/locale/dir_loop/loop new file mode 120000 index 000000000..5a2ea7b42 --- /dev/null +++ b/tests/samples/compose/usr/share/locale/dir_loop/loop @@ -0,0 +1 @@ +../dir_loop \ No newline at end of file diff --git a/tests/samples/compose/usr/share/metainfo/org.example.nonexistent-badlink.metainfo.xml b/tests/samples/compose/usr/share/metainfo/org.example.nonexistent-badlink.metainfo.xml new file mode 120000 index 000000000..bfda748b9 --- /dev/null +++ b/tests/samples/compose/usr/share/metainfo/org.example.nonexistent-badlink.metainfo.xml @@ -0,0 +1 @@ +/nonexistent \ No newline at end of file diff --git a/tests/test-compose.c b/tests/test-compose.c index cf8de26fd..fb2ca8ee9 100644 --- a/tests/test-compose.c +++ b/tests/test-compose.c @@ -703,7 +703,7 @@ test_compose_directory_unit (void) g_assert_true (ret); contents = asc_unit_get_contents (ASC_UNIT (dirunit)); - g_assert_cmpint (contents->len, ==, 15); + g_assert_cmpint (contents->len, ==, 16); as_sort_strings (contents); g_assert_cmpstr (g_ptr_array_index (contents, 0), ==, "/Raleway-Regular.ttf"); diff --git a/tests/test-xmldata.c b/tests/test-xmldata.c index c5b5f1db1..6f68e9d12 100644 --- a/tests/test-xmldata.c +++ b/tests/test-xmldata.c @@ -2293,13 +2293,12 @@ test_xml_rw_branding (void) static void test_xml_rw_developer (void) { - static const gchar - *xmldata_tags = "\n" - " org.example.DeveloperTest\n" - " \n" - " FreeDesktop.org Project\n" - " \n" - "\n"; + static const gchar *xmldata_tags = "\n" + " org.example.DeveloperTest\n" + " \n" + " FreeDesktop.org Project\n" + " \n" + "\n"; g_autoptr(AsComponent) cpt = NULL; g_autofree gchar *res = NULL; AsDeveloper *devp;