Skip to content
Snippets Groups Projects
Commit 697f07cb authored by Simon McVittie's avatar Simon McVittie
Browse files

exports: Sync with flatpak/flatpak#4352

This provides some more design-for-test that we can use when
unit-testing wrap-setup.c.

Forwarded: https://github.com/flatpak/flatpak/pull/4352


Applied-upstream: 1.11.3, commit:c392e5ad
Signed-off-by: default avatarSimon McVittie <smcv@collabora.com>
parent d21d4196
No related branches found
No related tags found
1 merge request!347Further preparation for !337
/* /*
* Taken from Flatpak, last updated: 1.11.2 * Taken from Flatpak, last updated: 1.11.2-15-gc392e5ad
* Copyright © 2014-2018 Red Hat, Inc * Copyright © 2014-2018 Red Hat, Inc
* SPDX-License-Identifier: LGPL-2.1-or-later * SPDX-License-Identifier: LGPL-2.1-or-later
* *
...@@ -65,8 +65,21 @@ FlatpakFilesystemMode flatpak_exports_path_get_mode (FlatpakExports *exports, ...@@ -65,8 +65,21 @@ FlatpakFilesystemMode flatpak_exports_path_get_mode (FlatpakExports *exports,
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakExports, flatpak_exports_free); G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakExports, flatpak_exports_free);
/*
* FlatpakExportsTestFlags:
* @FLATPAK_EXPORTS_TEST_FLAGS_AUTOFS: Pretend everything is an autofs.
*
* Flags used to provide mock behaviour during unit testing.
*/
typedef enum
{
FLATPAK_EXPORTS_TEST_FLAGS_AUTOFS = (1 << 0),
FLATPAK_EXPORTS_TEST_FLAGS_NONE = 0
} FlatpakExportsTestFlags;
void flatpak_exports_take_host_fd (FlatpakExports *exports, void flatpak_exports_take_host_fd (FlatpakExports *exports,
int fd); int fd);
void flatpak_exports_set_test_flags (FlatpakExports *exports,
FlatpakExportsTestFlags flags);
#endif /* __FLATPAK_EXPORTS_H__ */ #endif /* __FLATPAK_EXPORTS_H__ */
/* /*
* Taken from Flatpak, last updated: 1.11.2 * Taken from Flatpak, last updated: 1.11.2-15-gc392e5ad
* Copyright © 2014-2019 Red Hat, Inc * Copyright © 2014-2019 Red Hat, Inc
* SPDX-License-Identifier: LGPL-2.1-or-later * SPDX-License-Identifier: LGPL-2.1-or-later
* *
...@@ -111,6 +111,7 @@ struct _FlatpakExports ...@@ -111,6 +111,7 @@ struct _FlatpakExports
FlatpakFilesystemMode host_etc; FlatpakFilesystemMode host_etc;
FlatpakFilesystemMode host_os; FlatpakFilesystemMode host_os;
int host_fd; int host_fd;
FlatpakExportsTestFlags test_flags;
}; };
/* /*
...@@ -127,6 +128,13 @@ flatpak_exports_take_host_fd (FlatpakExports *exports, ...@@ -127,6 +128,13 @@ flatpak_exports_take_host_fd (FlatpakExports *exports,
exports->host_fd = fd; exports->host_fd = fd;
} }
void
flatpak_exports_set_test_flags (FlatpakExports *exports,
FlatpakExportsTestFlags flags)
{
exports->test_flags = flags;
}
static gboolean static gboolean
flatpak_exports_stat_in_host (FlatpakExports *exports, flatpak_exports_stat_in_host (FlatpakExports *exports,
const char *abs_path, const char *abs_path,
...@@ -140,7 +148,11 @@ flatpak_exports_stat_in_host (FlatpakExports *exports, ...@@ -140,7 +148,11 @@ flatpak_exports_stat_in_host (FlatpakExports *exports,
{ {
/* If abs_path is "/usr", then stat "usr" relative to host_fd. /* If abs_path is "/usr", then stat "usr" relative to host_fd.
* As a special case, if abs_path is "/", stat host_fd itself, * As a special case, if abs_path is "/", stat host_fd itself,
* due to the use of AT_EMPTY_PATH. */ * due to the use of AT_EMPTY_PATH.
*
* This won't work if ${host_fd}/${abs_path} contains symlinks
* that are absolute or otherwise escape from the mock root,
* so be careful not to do that in unit tests. */
return glnx_fstatat (exports->host_fd, &abs_path[1], buf, return glnx_fstatat (exports->host_fd, &abs_path[1], buf,
AT_EMPTY_PATH | flags, AT_EMPTY_PATH | flags,
error); error);
...@@ -156,6 +168,8 @@ flatpak_exports_readlink_in_host (FlatpakExports *exports, ...@@ -156,6 +168,8 @@ flatpak_exports_readlink_in_host (FlatpakExports *exports,
{ {
g_return_val_if_fail (abs_path[0] == '/', FALSE); g_return_val_if_fail (abs_path[0] == '/', FALSE);
/* Similar to flatpak_exports_stat_in_host, this assumes the mock root
* doesn't contain symlinks that escape from the mock root. */
if (exports->host_fd >= 0) if (exports->host_fd >= 0)
return glnx_readlinkat_malloc (exports->host_fd, &abs_path[1], return glnx_readlinkat_malloc (exports->host_fd, &abs_path[1],
NULL, error); NULL, error);
...@@ -163,6 +177,61 @@ flatpak_exports_readlink_in_host (FlatpakExports *exports, ...@@ -163,6 +177,61 @@ flatpak_exports_readlink_in_host (FlatpakExports *exports,
return glnx_readlinkat_malloc (AT_FDCWD, abs_path, NULL, error); return glnx_readlinkat_malloc (AT_FDCWD, abs_path, NULL, error);
} }
/* path must be absolute, but this is not checked because assertions
* are not async-signal-safe. */
static int
flatpak_exports_open_in_host_async_signal_safe (FlatpakExports *exports,
const char *abs_path,
int flags)
{
flags |= O_CLOEXEC;
if (exports->host_fd >= 0)
{
/* Similar to flatpak_exports_stat_in_host, this assumes the mock root
* doesn't contain symlinks that escape from the mock root. */
return openat (exports->host_fd, &abs_path[1],
flags);
}
return openat (AT_FDCWD, abs_path, flags);
}
static int
flatpak_exports_open_in_host (FlatpakExports *exports,
const char *abs_path,
int flags)
{
g_return_val_if_fail (abs_path[0] == '/', -1);
return flatpak_exports_open_in_host_async_signal_safe (exports, abs_path, flags);
}
static char *
flatpak_exports_resolve_link_in_host (FlatpakExports *exports,
const char *abs_path,
GError **error)
{
g_return_val_if_fail (abs_path[0] == '/', FALSE);
if (exports->host_fd >= 0)
{
g_autofree char *fd_path = g_strdup_printf ("/proc/self/fd/%d/",
exports->host_fd);
g_autofree char *real_path = g_strdup_printf ("%s%s", fd_path, &abs_path[1]);
g_autofree char *resolved = flatpak_resolve_link (real_path, error);
if (resolved == NULL)
return NULL;
if (!g_str_has_prefix (resolved, fd_path))
return glnx_null_throw (error, "Symbolic link escapes from mock root");
return g_strdup (resolved + strlen (fd_path) - 1);
}
return flatpak_resolve_link (abs_path, error);
}
static void static void
exported_path_free (ExportedPath *exported_path) exported_path_free (ExportedPath *exported_path)
{ {
...@@ -183,6 +252,7 @@ flatpak_exports_new (void) ...@@ -183,6 +252,7 @@ flatpak_exports_new (void)
void void
flatpak_exports_free (FlatpakExports *exports) flatpak_exports_free (FlatpakExports *exports)
{ {
glnx_close_fd (&exports->host_fd);
g_hash_table_destroy (exports->hash); g_hash_table_destroy (exports->hash);
g_free (exports); g_free (exports);
} }
...@@ -271,22 +341,24 @@ compare_eps (const ExportedPath *a, ...@@ -271,22 +341,24 @@ compare_eps (const ExportedPath *a,
/* This differs from g_file_test (path, G_FILE_TEST_IS_DIR) which /* This differs from g_file_test (path, G_FILE_TEST_IS_DIR) which
returns true if the path is a symlink to a dir */ returns true if the path is a symlink to a dir */
static gboolean static gboolean
path_is_dir (const char *path) path_is_dir (FlatpakExports *exports,
const char *path)
{ {
struct stat s; struct stat s;
if (lstat (path, &s) != 0) if (!flatpak_exports_stat_in_host (exports, path, &s, AT_SYMLINK_NOFOLLOW, NULL))
return FALSE; return FALSE;
return S_ISDIR (s.st_mode); return S_ISDIR (s.st_mode);
} }
static gboolean static gboolean
path_is_symlink (const char *path) path_is_symlink (FlatpakExports *exports,
const char *path)
{ {
struct stat s; struct stat s;
if (lstat (path, &s) != 0) if (!flatpak_exports_stat_in_host (exports, path, &s, AT_SYMLINK_NOFOLLOW, NULL))
return FALSE; return FALSE;
return S_ISLNK (s.st_mode); return S_ISLNK (s.st_mode);
...@@ -339,7 +411,9 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports, ...@@ -339,7 +411,9 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports,
{ {
if (!path_parent_is_mapped (keys, n_keys, exports->hash, path)) if (!path_parent_is_mapped (keys, n_keys, exports->hash, path))
{ {
g_autofree char *resolved = flatpak_resolve_link (path, NULL); g_autofree char *resolved = flatpak_exports_resolve_link_in_host (exports,
path,
NULL);
if (resolved) if (resolved)
{ {
g_autofree char *parent = g_path_get_dirname (path); g_autofree char *parent = g_path_get_dirname (path);
...@@ -352,7 +426,7 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports, ...@@ -352,7 +426,7 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports,
{ {
/* Mount a tmpfs to hide the subdirectory, but only if there /* Mount a tmpfs to hide the subdirectory, but only if there
is a pre-existing dir we can mount the path on. */ is a pre-existing dir we can mount the path on. */
if (path_is_dir (path)) if (path_is_dir (exports, path))
{ {
if (!path_parent_is_mapped (keys, n_keys, exports->hash, path)) if (!path_parent_is_mapped (keys, n_keys, exports->hash, path))
/* If the parent is not mapped, it will be a tmpfs, no need to mount another one */ /* If the parent is not mapped, it will be a tmpfs, no need to mount another one */
...@@ -363,7 +437,7 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports, ...@@ -363,7 +437,7 @@ flatpak_exports_append_bwrap_args (FlatpakExports *exports,
} }
else if (ep->mode == FAKE_MODE_DIR) else if (ep->mode == FAKE_MODE_DIR)
{ {
if (path_is_dir (path)) if (path_is_dir (exports, path))
flatpak_bwrap_add_args (bwrap, "--dir", path, NULL); flatpak_bwrap_add_args (bwrap, "--dir", path, NULL);
} }
else else
...@@ -509,6 +583,7 @@ flatpak_exports_path_get_mode (FlatpakExports *exports, ...@@ -509,6 +583,7 @@ flatpak_exports_path_get_mode (FlatpakExports *exports,
g_qsort_with_data (keys, n_keys, sizeof (char *), (GCompareDataFunc) flatpak_strcmp0_ptr, NULL); g_qsort_with_data (keys, n_keys, sizeof (char *), (GCompareDataFunc) flatpak_strcmp0_ptr, NULL);
/* Syntactic canonicalization only, no need to use host_fd */
path = canonical = flatpak_canonicalize_filename (path); path = canonical = flatpak_canonicalize_filename (path);
parts = g_strsplit (path + 1, "/", -1); parts = g_strsplit (path + 1, "/", -1);
...@@ -526,9 +601,13 @@ flatpak_exports_path_get_mode (FlatpakExports *exports, ...@@ -526,9 +601,13 @@ flatpak_exports_path_get_mode (FlatpakExports *exports,
if (path_is_mapped (keys, n_keys, exports->hash, path_builder->str, &is_readonly)) if (path_is_mapped (keys, n_keys, exports->hash, path_builder->str, &is_readonly))
{ {
if (lstat (path_builder->str, &st) != 0) g_autoptr(GError) stat_error = NULL;
if (!flatpak_exports_stat_in_host (exports, path_builder->str, &st, AT_SYMLINK_NOFOLLOW, &stat_error))
{ {
if (errno == ENOENT && parts[i + 1] == NULL && !is_readonly) if (g_error_matches (stat_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
parts[i + 1] == NULL &&
!is_readonly)
{ {
/* Last element was mapped but isn't there, this is /* Last element was mapped but isn't there, this is
* OK (used for the save case) if we the parent is * OK (used for the save case) if we the parent is
...@@ -543,7 +622,9 @@ flatpak_exports_path_get_mode (FlatpakExports *exports, ...@@ -543,7 +622,9 @@ flatpak_exports_path_get_mode (FlatpakExports *exports,
if (S_ISLNK (st.st_mode)) if (S_ISLNK (st.st_mode))
{ {
g_autofree char *resolved = flatpak_resolve_link (path_builder->str, NULL); g_autofree char *resolved = flatpak_exports_resolve_link_in_host (exports,
path_builder->str,
NULL);
g_autoptr(GString) path2_builder = NULL; g_autoptr(GString) path2_builder = NULL;
int j; int j;
...@@ -618,7 +699,8 @@ do_export_path (FlatpakExports *exports, ...@@ -618,7 +699,8 @@ do_export_path (FlatpakExports *exports,
* have to mess with forks and stuff to be able to handle the timeout. * have to mess with forks and stuff to be able to handle the timeout.
*/ */
static gboolean static gboolean
check_if_autofs_works (const char *path) check_if_autofs_works (FlatpakExports *exports,
const char *path)
{ {
int selfpipe[2]; int selfpipe[2];
struct timeval timeout; struct timeval timeout;
...@@ -627,6 +709,8 @@ check_if_autofs_works (const char *path) ...@@ -627,6 +709,8 @@ check_if_autofs_works (const char *path)
int res; int res;
int wstatus; int wstatus;
g_return_val_if_fail (path[0] == '/', FALSE);
if (pipe2 (selfpipe, O_CLOEXEC) == -1) if (pipe2 (selfpipe, O_CLOEXEC) == -1)
return FALSE; return FALSE;
...@@ -646,7 +730,10 @@ check_if_autofs_works (const char *path) ...@@ -646,7 +730,10 @@ check_if_autofs_works (const char *path)
/* Note: open, close and _exit are signal-async-safe, so it is ok to call in the child after fork */ /* Note: open, close and _exit are signal-async-safe, so it is ok to call in the child after fork */
close (selfpipe[0]); /* Close unused read end */ close (selfpipe[0]); /* Close unused read end */
int dir_fd = open (path, O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_DIRECTORY);
int dir_fd = flatpak_exports_open_in_host_async_signal_safe (exports,
path,
O_RDONLY | O_NONBLOCK | O_DIRECTORY);
_exit (dir_fd == -1 ? 1 : 0); _exit (dir_fd == -1 ? 1 : 0);
} }
...@@ -678,6 +765,12 @@ check_if_autofs_works (const char *path) ...@@ -678,6 +765,12 @@ check_if_autofs_works (const char *path)
if (!WIFEXITED (wstatus) || WEXITSTATUS (wstatus) != 0) if (!WIFEXITED (wstatus) || WEXITSTATUS (wstatus) != 0)
return FALSE; return FALSE;
if (G_UNLIKELY (exports->test_flags & FLATPAK_EXPORTS_TEST_FLAGS_AUTOFS))
{
if (strcmp (path, "/broken-autofs") == 0)
return FALSE;
}
return TRUE; return TRUE;
} }
...@@ -710,7 +803,7 @@ _exports_path_expose (FlatpakExports *exports, ...@@ -710,7 +803,7 @@ _exports_path_expose (FlatpakExports *exports,
} }
/* Check if it exists at all */ /* Check if it exists at all */
o_path_fd = open (path, O_PATH | O_NOFOLLOW | O_CLOEXEC); o_path_fd = flatpak_exports_open_in_host (exports, path, O_PATH | O_NOFOLLOW);
if (o_path_fd == -1) if (o_path_fd == -1)
return FALSE; return FALSE;
...@@ -728,15 +821,18 @@ _exports_path_expose (FlatpakExports *exports, ...@@ -728,15 +821,18 @@ _exports_path_expose (FlatpakExports *exports,
if (fstatfs (o_path_fd, &stfs) != 0) if (fstatfs (o_path_fd, &stfs) != 0)
return FALSE; return FALSE;
if (stfs.f_type == AUTOFS_SUPER_MAGIC) if (stfs.f_type == AUTOFS_SUPER_MAGIC ||
(G_UNLIKELY (exports->test_flags & FLATPAK_EXPORTS_TEST_FLAGS_AUTOFS) &&
S_ISDIR (st.st_mode)))
{ {
if (!check_if_autofs_works (path)) if (!check_if_autofs_works (exports, path))
{ {
g_debug ("ignoring blocking autofs path %s", path); g_debug ("ignoring blocking autofs path %s", path);
return FALSE; return FALSE;
} }
} }
/* Syntactic canonicalization only, no need to use host_fd */
path = canonical = flatpak_canonicalize_filename (path); path = canonical = flatpak_canonicalize_filename (path);
for (i = 0; dont_export_in[i] != NULL; i++) for (i = 0; dont_export_in[i] != NULL; i++)
...@@ -770,9 +866,9 @@ _exports_path_expose (FlatpakExports *exports, ...@@ -770,9 +866,9 @@ _exports_path_expose (FlatpakExports *exports,
if (slash) if (slash)
*slash = 0; *slash = 0;
if (path_is_symlink (path) && !never_export_as_symlink (path)) if (path_is_symlink (exports, path) && !never_export_as_symlink (path))
{ {
g_autofree char *resolved = flatpak_resolve_link (path, NULL); g_autofree char *resolved = flatpak_exports_resolve_link_in_host (exports, path, NULL);
g_autofree char *new_target = NULL; g_autofree char *new_target = NULL;
if (resolved) if (resolved)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment