From cbd9e214270c5dcf7c8942173396f49335b5f041 Mon Sep 17 00:00:00 2001 From: Ludovico de Nittis <ludovico.denittis@collabora.com> Date: Wed, 2 Sep 2020 15:03:18 +0200 Subject: [PATCH] utils: Add pv_file_test_in_sysroot With this new function it will be possible to test the existence or properties of a given filename under a specific sysroot. Signed-off-by: Ludovico de Nittis <ludovico.denittis@collabora.com> --- src/utils.c | 73 +++++++++++++++++++++++++++++++++++ src/utils.h | 4 ++ tests/utils.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) diff --git a/src/utils.c b/src/utils.c index e646399bd..0ca0fa46d 100644 --- a/src/utils.c +++ b/src/utils.c @@ -40,6 +40,8 @@ #include "flatpak-utils-base-private.h" #include "flatpak-utils-private.h" +#include "resolve-in-sysroot.h" + static void child_setup_cb (gpointer user_data) { @@ -926,3 +928,74 @@ pv_terminate_all_child_processes (GTimeSpan wait_period, return TRUE; } + +/** + * pv_file_test_in_sysroot: + * @sysroot: (type filename): A path used as the root + * @filename: (type filename): A path below the root directory, either + * absolute or relative (to the root) + * @test: The test to perform on the resolved file in @sysroot. + * G_FILE_TEST_IS_SYMLINK is not a valid GFileTest value because the + * path is resolved following symlinks too. + * + * Returns: %TRUE if the @filename resolved in @sysroot passes the @test. + */ +gboolean +pv_file_test_in_sysroot (const char *sysroot, + const char *filename, + GFileTest test) +{ + glnx_autofd int file_fd = -1; + glnx_autofd int sysroot_fd = -1; + struct stat stat_buf; + g_autofree gchar *file_realpath_in_sysroot = NULL; + g_autoptr(GError) error = NULL; + + g_return_val_if_fail (sysroot != NULL, FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + /* We reject G_FILE_TEST_IS_SYMLINK because the provided filename is resolved + * in sysroot, following the eventual symlinks too. So it is not possible for + * the resolved filename to be a symlink */ + g_return_val_if_fail ((test & (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE + | G_FILE_TEST_IS_REGULAR + | G_FILE_TEST_IS_DIR)) == test, FALSE); + + if (!glnx_opendirat (-1, sysroot, FALSE, &sysroot_fd, &error)) + { + g_debug ("An error occurred trying to open %s: %s", sysroot, + error->message); + return FALSE; + } + + file_fd = pv_resolve_in_sysroot (sysroot_fd, + filename, PV_RESOLVE_FLAGS_NONE, + &file_realpath_in_sysroot, &error); + + if (file_fd < 0) + { + g_debug ("An error occurred trying to resolve %s in sysroot: %s", + filename, error->message); + return FALSE; + } + + if (fstat (file_fd, &stat_buf) != 0) + { + g_debug ("fstat %s/%s: %s", + sysroot, file_realpath_in_sysroot, g_strerror (errno)); + return FALSE; + } + + if (test & G_FILE_TEST_EXISTS) + return TRUE; + + if ((test & G_FILE_TEST_IS_EXECUTABLE) && (stat_buf.st_mode & 0111)) + return TRUE; + + if ((test & G_FILE_TEST_IS_REGULAR) && S_ISREG (stat_buf.st_mode)) + return TRUE; + + if ((test & G_FILE_TEST_IS_DIR) && S_ISDIR (stat_buf.st_mode)) + return TRUE; + + return FALSE; +} diff --git a/src/utils.h b/src/utils.h index 0966efedb..811c0cb39 100644 --- a/src/utils.h +++ b/src/utils.h @@ -73,3 +73,7 @@ gboolean pv_wait_for_child_processes (pid_t main_process, gboolean pv_terminate_all_child_processes (GTimeSpan wait_period, GTimeSpan grace_period, GError **error); + +gboolean pv_file_test_in_sysroot (const char *sysroot, + const char *filename, + GFileTest test); diff --git a/tests/utils.c b/tests/utils.c index fd5a2977f..57f944ba3 100644 --- a/tests/utils.c +++ b/tests/utils.c @@ -278,6 +278,107 @@ test_search_path_append (Fixture *f, g_assert_cmpstr (str->str, ==, "/bin:/usr/bin:/usr/bin"); } +typedef struct +{ + const char *name; + mode_t mode; +} File; + +typedef struct +{ + const char *name; + const char *target; +} Symlink; + +typedef struct +{ + const char *path; + GFileTest test; + gboolean expected_result; +} InSysrootTest; + +static void +test_file_in_sysroot (Fixture *f, + gconstpointer context) +{ + static const char * const prepare_dirs[] = + { + "dir1/dir2/dir3", + }; + + static const File prepare_files[] = + { + { "dir1/file1", 0600 }, + { "dir1/dir2/file2", 0600 }, + { "dir1/exec1", 0700 }, + }; + + static const Symlink prepare_symlinks[] = + { + { "dir1/dir2/symlink_to_dir3", "dir3" }, + { "dir1/dir2/symlink_to_file2", "file2" }, + { "dir1/dir2/sym_to_sym_to_file2", "symlink_to_file2" }, + { "dir1/abs_symlink_to_run", "/run" }, + }; + + static const InSysrootTest tests[] = + { + { "dir1", G_FILE_TEST_IS_DIR, TRUE }, + { "dir1", G_FILE_TEST_EXISTS, TRUE }, + { "/dir1", G_FILE_TEST_EXISTS, TRUE }, + { "dir1/dir2", G_FILE_TEST_IS_DIR, TRUE }, + /* These gets solved in sysroot, following symlinks too */ + { "dir1/dir2/symlink_to_dir3", G_FILE_TEST_IS_DIR, TRUE }, + { "dir1/dir2/sym_to_sym_to_file2", G_FILE_TEST_IS_REGULAR, TRUE }, + { "dir1/abs_symlink_to_run", G_FILE_TEST_IS_DIR, FALSE }, + { "dir1/missing", G_FILE_TEST_EXISTS, FALSE }, + { "dir1/file1", G_FILE_TEST_IS_REGULAR, TRUE }, + { "dir1/file1", (G_FILE_TEST_IS_DIR | G_FILE_TEST_IS_EXECUTABLE), FALSE }, + { "dir1/exec1", G_FILE_TEST_IS_REGULAR, TRUE }, + { "dir1/exec1", G_FILE_TEST_IS_EXECUTABLE, TRUE }, + }; + + g_autoptr(GError) error = NULL; + g_auto(GLnxTmpDir) tmpdir = { FALSE }; + gsize i; + + glnx_mkdtemp ("test-XXXXXX", 0700, &tmpdir, &error); + g_assert_no_error (error); + + for (i = 0; i < G_N_ELEMENTS (prepare_dirs); i++) + { + const char *it = prepare_dirs[i]; + + glnx_shutil_mkdir_p_at (tmpdir.fd, it, 0700, NULL, &error); + g_assert_no_error (error); + } + + for (i = 0; i < G_N_ELEMENTS (prepare_files); i++) + { + const File *it = &prepare_files[i]; + + glnx_autofd int fd = openat (tmpdir.fd, it->name, O_WRONLY|O_CREAT, it->mode); + if (fd == -1) + g_error ("openat %s: %s", it->name, g_strerror (errno)); + } + + for (i = 0; i < G_N_ELEMENTS (prepare_symlinks); i++) + { + const Symlink *it = &prepare_symlinks[i]; + + if (symlinkat (it->target, tmpdir.fd, it->name) != 0) + g_error ("symlinkat %s: %s", it->name, g_strerror (errno)); + } + + for (i = 0; i < G_N_ELEMENTS (tests); i++) + { + const InSysrootTest *it = &tests[i]; + + g_assert_cmpint (pv_file_test_in_sysroot (tmpdir.path, it->path, it->test), + ==, it->expected_result); + } +} + int main (int argc, char **argv) @@ -294,6 +395,8 @@ main (int argc, 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); + g_test_add ("/test-file-in-sysroot", Fixture, NULL, + setup, test_file_in_sysroot, teardown); return g_test_run (); } -- GitLab