mirror of
https://github.com/flatpak/flatpak.git
synced 2026-03-20 08:00:18 -04:00
run: Don't block on blocking autofs mounts
When we start a sandbox and one of the directories we bind-mount into the sandbox, for example one of the top dirs like /boot, is an autofs, then the bind mount will trigger an automount. Often this is not really a problem, but in some cases (such as a missing device or a network mount that is not available) this can block for potentially a very long time. Ideally we would like to avoid this by not triggering the automount when bind-mounting, but that doesn't seem possible with the current kernel APIs. So, instead we try to detect any autofs paths and try to do a blocking operation on them, if this takes more than 200 milliseconds we assume the mountpoint is broken and ignore it. There are various technical details here: We can check for autofs using open(O_PATH) + fstatfs() and looking for f_type == AUTOFS_SUPER_MAGIC (as described in the open manpage). The blocking operation we do is open(O_DIRECTORY), but to allow this to timeout we need to fork() and do this in the child, which we can kill after timing out, whick we detect via select() using a self-pipe. This fixes https://github.com/flatpak/flatpak/issues/1633 Closes: #1648 Approved by: alexlarsson
This commit is contained in:
committed by
Atomic Bot
parent
c250857c59
commit
97dabb10a8
@@ -27,7 +27,10 @@
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <sys/personality.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <grp.h>
|
||||
#include <unistd.h>
|
||||
#include <gio/gunixfdlist.h>
|
||||
@@ -400,6 +403,75 @@ do_export_path (FlatpakExports *exports,
|
||||
g_hash_table_replace (exports->hash, ep->path, ep);
|
||||
}
|
||||
|
||||
/* AUTOFS mounts are tricky, as using them as a source in a bind mount
|
||||
* causes the mount to trigger, which can take a long time (or forever)
|
||||
* waiting for a device or network mount. We try to open the directory
|
||||
* but time out after a while, ignoring the mount. Unfortunately we
|
||||
* have to mess with forks and stuff to be able to handle the timeout.
|
||||
*/
|
||||
static gboolean
|
||||
check_if_autofs_works (const char *path)
|
||||
{
|
||||
int selfpipe[2];
|
||||
struct timeval timeout;
|
||||
pid_t pid;
|
||||
fd_set rfds;
|
||||
int res;
|
||||
int wstatus;
|
||||
|
||||
if (pipe (selfpipe) == -1)
|
||||
return FALSE;
|
||||
|
||||
fcntl (selfpipe[0], F_SETFL, fcntl (selfpipe[0], F_GETFL) | O_NONBLOCK);
|
||||
fcntl (selfpipe[1], F_SETFL, fcntl (selfpipe[1], F_GETFL) | O_NONBLOCK);
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
close (selfpipe[0]);
|
||||
close (selfpipe[1]);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
/* 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 */
|
||||
int dir_fd = open (path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY);
|
||||
_exit (dir_fd == -1 ? 1 : 0);
|
||||
}
|
||||
|
||||
/* Parent */
|
||||
close (selfpipe[1]); /* Close unused write end */
|
||||
|
||||
/* 200 msec timeout*/
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 200 * 1000;
|
||||
|
||||
FD_ZERO (&rfds);
|
||||
FD_SET (selfpipe[0], &rfds);
|
||||
res = select (selfpipe[0]+1, &rfds, NULL, NULL, &timeout);
|
||||
|
||||
close (selfpipe[0]);
|
||||
|
||||
if (res == -1 /* Error */ || res == 0) /* Timeout */
|
||||
{
|
||||
/* Kill, but then waitpid to avoid zombie */
|
||||
kill (pid, SIGKILL);
|
||||
}
|
||||
|
||||
if (waitpid (pid, &wstatus, 0) != pid)
|
||||
return FALSE;
|
||||
|
||||
if (res == -1 /* Error */ || res == 0) /* Timeout */
|
||||
return FALSE;
|
||||
|
||||
if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* We use level to avoid infinite recursion */
|
||||
static gboolean
|
||||
@@ -410,8 +482,10 @@ _exports_path_expose (FlatpakExports *exports,
|
||||
{
|
||||
g_autofree char *canonical = NULL;
|
||||
struct stat st;
|
||||
struct statfs stfs;
|
||||
char *slash;
|
||||
int i;
|
||||
glnx_autofd int o_path_fd = -1;
|
||||
|
||||
if (level > 40) /* 40 is the current kernel ELOOP check */
|
||||
{
|
||||
@@ -426,7 +500,11 @@ _exports_path_expose (FlatpakExports *exports,
|
||||
}
|
||||
|
||||
/* Check if it exists at all */
|
||||
if (lstat (path, &st) != 0)
|
||||
o_path_fd = open (path, O_PATH | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (o_path_fd == -1)
|
||||
return FALSE;
|
||||
|
||||
if (fstat (o_path_fd, &st) != 0)
|
||||
return FALSE;
|
||||
|
||||
/* Don't expose weird things */
|
||||
@@ -436,6 +514,19 @@ _exports_path_expose (FlatpakExports *exports,
|
||||
S_ISSOCK (st.st_mode)))
|
||||
return FALSE;
|
||||
|
||||
/* O_PATH + fstatfs is the magic that we need to statfs without automounting the target */
|
||||
if (fstatfs (o_path_fd, &stfs) != 0)
|
||||
return FALSE;
|
||||
|
||||
if (stfs.f_type == AUTOFS_SUPER_MAGIC)
|
||||
{
|
||||
if (!check_if_autofs_works (path))
|
||||
{
|
||||
g_debug ("ignoring blocking autofs path %s", path);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
path = canonical = flatpak_canonicalize_filename (path);
|
||||
|
||||
for (i = 0; dont_export_in[i] != NULL; i++)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <sys/personality.h>
|
||||
#include <grp.h>
|
||||
#include <unistd.h>
|
||||
@@ -1793,6 +1794,9 @@ prepend_bwrap_argv_wrapper (GPtrArray *argv,
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
glnx_autofd int o_path_fd = -1;
|
||||
struct statfs stfs;
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dir_iter, &dent, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
@@ -1802,6 +1806,11 @@ prepend_bwrap_argv_wrapper (GPtrArray *argv,
|
||||
if (strcmp (dent->d_name, ".flatpak-info") == 0)
|
||||
continue;
|
||||
|
||||
/* O_PATH + fstatfs is the magic that we need to statfs without automounting the target */
|
||||
o_path_fd = openat (dir_iter.fd, dent->d_name, O_PATH | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (o_path_fd == -1 || fstatfs (o_path_fd, &stfs) != 0 || stfs.f_type == AUTOFS_SUPER_MAGIC)
|
||||
continue; /* AUTOFS mounts are risky and can cause us to block (see issue #1633), so ignore it. Its unlikely the proxy needs such a directory. */
|
||||
|
||||
if (dent->d_type == DT_DIR)
|
||||
{
|
||||
if (strcmp (dent->d_name, "tmp") == 0 ||
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include <ostree.h>
|
||||
#include <json-glib/json-glib.h>
|
||||
|
||||
#define AUTOFS_SUPER_MAGIC 0x0187
|
||||
|
||||
typedef enum {
|
||||
FLATPAK_HOST_COMMAND_FLAGS_CLEAR_ENV = 1 << 0,
|
||||
} FlatpakHostCommandFlags;
|
||||
|
||||
Reference in New Issue
Block a user