-
Simon McVittie authored
This is preparation for combining pressure-vessel with steam-runtime-tools. Signed-off-by:
Simon McVittie <smcv@collabora.com>
Simon McVittie authoredThis is preparation for combining pressure-vessel with steam-runtime-tools. Signed-off-by:
Simon McVittie <smcv@collabora.com>
flatpak-exports.c 25.10 KiB
/*
* Taken from Flatpak, last updated: 1.9.x commit 1.8.0-74-g354b9a22
* Copyright © 2014-2019 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Alexander Larsson <alexl@redhat.com>
*/
#include "config.h"
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#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>
#include <gio/gio.h>
#include "libglnx/libglnx.h"
#include "flatpak-exports-private.h"
#include "flatpak-run-private.h"
#include "flatpak-utils-base-private.h"
#include "flatpak-utils-private.h"
#include "flatpak-error.h"
/* To keep this more similar to the original file, we explicitly disable
* these warnings rather than fixing them */
#if defined(__GNUC__) && __GNUC__ >= 8
# pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wtype-limits"
/* We don't want to export paths pointing into these, because they are readonly
(so we can't create mountpoints there) and don't match what's on the host anyway.
flatpak_abs_usrmerged_dirs get the same treatment without having to be listed
here. */
const char *dont_export_in[] = {
"/usr", "/etc", "/app", "/dev", "/proc", NULL
};
static char *
make_relative (const char *base, const char *path)
{
GString *s = g_string_new ("");
while (*base != 0)
{
while (*base == '/')
base++;
if (*base != 0)
g_string_append (s, "../");
while (*base != '/' && *base != 0)
base++;
}
while (*path == '/')
path++;
g_string_append (s, path);
return g_string_free (s, FALSE);
}
#define FAKE_MODE_DIR -1 /* Ensure a dir, either on tmpfs or mapped parent */
#define FAKE_MODE_TMPFS FLATPAK_FILESYSTEM_MODE_NONE
#define FAKE_MODE_SYMLINK G_MAXINT
static inline gboolean
is_export_mode (int mode)
{
return ((mode >= FLATPAK_FILESYSTEM_MODE_NONE
&& mode <= FLATPAK_FILESYSTEM_MODE_LAST)
|| mode == FAKE_MODE_DIR
|| mode == FAKE_MODE_SYMLINK);
}
typedef struct
{
char *path;
gint mode;
} ExportedPath;
struct _FlatpakExports
{
GHashTable *hash;
FlatpakFilesystemMode host_etc;
FlatpakFilesystemMode host_os;
int host_fd;
};
/*
* When populating /run/host, pretend @fd was the root of the host
* filesystem.
*/
void
flatpak_exports_take_host_fd (FlatpakExports *exports,
int fd)
{
glnx_close_fd (&exports->host_fd);
if (fd >= 0)
exports->host_fd = fd;
}
static gboolean
flatpak_exports_stat_in_host (FlatpakExports *exports,
const char *abs_path,
struct stat *buf,
int flags,
GError **error)
{
g_return_val_if_fail (abs_path[0] == '/', FALSE);
if (exports->host_fd >= 0)
{
/* If abs_path is "/usr", then stat "usr" relative to host_fd.
* As a special case, if abs_path is "/", stat host_fd itself,
* due to the use of AT_EMPTY_PATH. */
return glnx_fstatat (exports->host_fd, &abs_path[1], buf,
AT_EMPTY_PATH | flags,
error);
}
return glnx_fstatat (AT_FDCWD, abs_path, buf, flags, error);
}
static gchar *
flatpak_exports_readlink_in_host (FlatpakExports *exports,
const char *abs_path,
GError **error)
{
g_return_val_if_fail (abs_path[0] == '/', FALSE);
if (exports->host_fd >= 0)
return glnx_readlinkat_malloc (exports->host_fd, &abs_path[1],
NULL, error);
return glnx_readlinkat_malloc (AT_FDCWD, abs_path, NULL, error);
}
static void
exported_path_free (ExportedPath *exported_path)
{
g_free (exported_path->path);
g_free (exported_path);
}
FlatpakExports *
flatpak_exports_new (void)
{
FlatpakExports *exports = g_new0 (FlatpakExports, 1);
exports->hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GFreeFunc) exported_path_free);
exports->host_fd = -1;
return exports;
}
void
flatpak_exports_free (FlatpakExports *exports)
{
g_hash_table_destroy (exports->hash);
g_free (exports);
}
/* Returns TRUE if the location of this export
is not visible due to parents being exported */
static gboolean
path_parent_is_mapped (const char **keys,
guint n_keys,
GHashTable *hash_table,
const char *path)
{
guint i;
gboolean is_mapped = FALSE;
/* The keys are sorted so shorter (i.e. parents) are first */
for (i = 0; i < n_keys; i++)
{
const char *mounted_path = keys[i];
ExportedPath *ep = g_hash_table_lookup (hash_table, mounted_path);
g_assert (is_export_mode (ep->mode));
if (flatpak_has_path_prefix (path, mounted_path) &&
(strcmp (path, mounted_path) != 0))
{
/* FAKE_MODE_DIR has same mapped value as parent */
if (ep->mode == FAKE_MODE_DIR)
continue;
is_mapped = ep->mode != FAKE_MODE_TMPFS;
}
}
return is_mapped;
}
static gboolean
path_is_mapped (const char **keys,
guint n_keys,
GHashTable *hash_table,
const char *path,
gboolean *is_readonly_out)
{
guint i;
gboolean is_mapped = FALSE;
gboolean is_readonly = FALSE;
/* The keys are sorted so shorter (i.e. parents) are first */
for (i = 0; i < n_keys; i++)
{
const char *mounted_path = keys[i];
ExportedPath *ep = g_hash_table_lookup (hash_table, mounted_path);
g_assert (is_export_mode (ep->mode));
if (flatpak_has_path_prefix (path, mounted_path))
{
/* FAKE_MODE_DIR has same mapped value as parent */
if (ep->mode == FAKE_MODE_DIR)
continue;
if (ep->mode == FAKE_MODE_SYMLINK)
is_mapped = strcmp (path, mounted_path) == 0;
else
is_mapped = ep->mode != FAKE_MODE_TMPFS;
if (is_mapped)
is_readonly = ep->mode == FLATPAK_FILESYSTEM_MODE_READ_ONLY;
else
is_readonly = FALSE;
}
}
*is_readonly_out = is_readonly;
return is_mapped;
}
static gint
compare_eps (const ExportedPath *a,
const ExportedPath *b)
{
return g_strcmp0 (a->path, b->path);
}
/* This differs from g_file_test (path, G_FILE_TEST_IS_DIR) which
returns true if the path is a symlink to a dir */
static gboolean
path_is_dir (const char *path)
{
struct stat s;
if (lstat (path, &s) != 0)
return FALSE;
return S_ISDIR (s.st_mode);
}
static gboolean
path_is_symlink (const char *path)
{
struct stat s;
if (lstat (path, &s) != 0)
return FALSE;
return S_ISLNK (s.st_mode);
}
/*
* @name: A file or directory below /etc
* @test: How we test whether it is suitable
*
* The paths in /etc that are required if we want to make use of the
* host /usr (and /lib, and so on).
*/
typedef struct
{
const char *name;
int ifmt;
} LibsNeedEtc;
static const LibsNeedEtc libs_need_etc[] =
{
/* glibc */
{ "ld.so.cache", S_IFREG },
/* Used for executables and a few libraries on e.g. Debian */
{ "alternatives", S_IFDIR }
};
void
flatpak_exports_append_bwrap_args (FlatpakExports *exports,
FlatpakBwrap *bwrap)
{
guint n_keys;
g_autofree const char **keys = (const char **) g_hash_table_get_keys_as_array (exports->hash, &n_keys);
g_autoptr(GList) eps = NULL;
GList *l;
struct stat buf;
eps = g_hash_table_get_values (exports->hash);
eps = g_list_sort (eps, (GCompareFunc) compare_eps);
g_qsort_with_data (keys, n_keys, sizeof (char *), (GCompareDataFunc) flatpak_strcmp0_ptr, NULL);
for (l = eps; l != NULL; l = l->next)
{
ExportedPath *ep = l->data;
const char *path = ep->path;
g_assert (is_export_mode (ep->mode));
if (ep->mode == FAKE_MODE_SYMLINK)
{
if (!path_parent_is_mapped (keys, n_keys, exports->hash, path))
{
g_autofree char *resolved = flatpak_resolve_link (path, NULL);
if (resolved)
{
g_autofree char *parent = g_path_get_dirname (path);
g_autofree char *relative = make_relative (parent, resolved);
flatpak_bwrap_add_args (bwrap, "--symlink", relative, path, NULL);
}
}
}
else if (ep->mode == FAKE_MODE_TMPFS)
{
/* Mount a tmpfs to hide the subdirectory, but only if there
is a pre-existing dir we can mount the path on. */
if (path_is_dir (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 */
flatpak_bwrap_add_args (bwrap, "--dir", path, NULL);
else
flatpak_bwrap_add_args (bwrap, "--tmpfs", path, NULL);
}
}
else if (ep->mode == FAKE_MODE_DIR)
{
if (path_is_dir (path))
flatpak_bwrap_add_args (bwrap, "--dir", path, NULL);
}
else
{
flatpak_bwrap_add_args (bwrap,
(ep->mode == FLATPAK_FILESYSTEM_MODE_READ_ONLY) ? "--ro-bind" : "--bind",
path, path, NULL);
}
}
g_assert (exports->host_os >= FLATPAK_FILESYSTEM_MODE_NONE);
g_assert (exports->host_os <= FLATPAK_FILESYSTEM_MODE_LAST);
if (exports->host_os != FLATPAK_FILESYSTEM_MODE_NONE)
{
const char *os_bind_mode = "--bind";
int i;
if (exports->host_os == FLATPAK_FILESYSTEM_MODE_READ_ONLY)
os_bind_mode = "--ro-bind";
if (flatpak_exports_stat_in_host (exports, "/usr", &buf, 0, NULL) &&
S_ISDIR (buf.st_mode))
flatpak_bwrap_add_args (bwrap,
os_bind_mode, "/usr", "/run/host/usr", NULL);
for (i = 0; flatpak_abs_usrmerged_dirs[i] != NULL; i++)
{
const char *subdir = flatpak_abs_usrmerged_dirs[i];
g_autofree char *target = NULL;
g_autofree char *run_host_subdir = NULL;
g_assert (subdir[0] == '/');
/* e.g. /run/host/lib32 */
run_host_subdir = g_strconcat ("/run/host", subdir, NULL);
target = flatpak_exports_readlink_in_host (exports, subdir, NULL);
if (target != NULL &&
g_str_has_prefix (target, "usr/"))
{
/* e.g. /lib32 is a relative symlink to usr/lib32, or
* on Arch Linux, /lib64 is a relative symlink to usr/lib;
* keep it relative */
flatpak_bwrap_add_args (bwrap,
"--symlink", target, run_host_subdir,
NULL);
}
else if (target != NULL &&
g_str_has_prefix (target, "/usr/"))
{
/* e.g. /lib32 is an absolute symlink to /usr/lib32; make
* it a relative symlink to usr/lib32 instead by skipping
* the '/' */
flatpak_bwrap_add_args (bwrap,
"--symlink", target + 1, run_host_subdir,
NULL);
}
else if (flatpak_exports_stat_in_host (exports, subdir, &buf, 0, NULL) &&
S_ISDIR (buf.st_mode))
{
/* e.g. /lib32 is a symlink to /opt/compat/ia32/lib,
* or is a plain directory because the host OS has not
* undergone the /usr merge; bind-mount the directory instead */
flatpak_bwrap_add_args (bwrap,
os_bind_mode, subdir, run_host_subdir,
NULL);
}
}
if (exports->host_etc == FLATPAK_FILESYSTEM_MODE_NONE)
{
guint i;
/* We are exposing the host /usr (and friends) but not the
* host /etc. Additionally expose just enough of /etc to make
* things that want to read /usr work as expected.
*
* (If exports->host_etc is nonzero, we'll do this as part of
* /etc instead.) */
for (i = 0; i < G_N_ELEMENTS (libs_need_etc); i++)
{
const LibsNeedEtc *item = &libs_need_etc[i];
g_autofree gchar *host_path = g_strconcat ("/etc/", item->name, NULL);
if (flatpak_exports_stat_in_host (exports, host_path,
&buf, 0, NULL) &&
(buf.st_mode & S_IFMT) == item->ifmt)
{
g_autofree gchar *run_host_path = g_strconcat ("/run/host/etc/", item->name, NULL);
flatpak_bwrap_add_args (bwrap,
os_bind_mode, host_path, run_host_path,
NULL);
}
}
}
}
g_assert (exports->host_etc >= FLATPAK_FILESYSTEM_MODE_NONE);
g_assert (exports->host_etc <= FLATPAK_FILESYSTEM_MODE_LAST);
if (exports->host_etc != FLATPAK_FILESYSTEM_MODE_NONE)
{
const char *etc_bind_mode = "--bind";
if (exports->host_etc == FLATPAK_FILESYSTEM_MODE_READ_ONLY)
etc_bind_mode = "--ro-bind";
if (flatpak_exports_stat_in_host (exports, "/etc", &buf, 0, NULL) &&
S_ISDIR (buf.st_mode))
flatpak_bwrap_add_args (bwrap,
etc_bind_mode, "/etc", "/run/host/etc", NULL);
}
/* As per the os-release specification https://www.freedesktop.org/software/systemd/man/os-release.html
* always read-only bind-mount /etc/os-release if it exists, or /usr/lib/os-release as a fallback from
* the host into the application's /run/host */
if (flatpak_exports_stat_in_host (exports, "/etc/os-release", &buf, 0, NULL))
flatpak_bwrap_add_args (bwrap, "--ro-bind", "/etc/os-release", "/run/host/os-release", NULL);
else if (flatpak_exports_stat_in_host (exports, "/usr/lib/os-release", &buf, 0, NULL))
flatpak_bwrap_add_args (bwrap, "--ro-bind", "/usr/lib/os-release", "/run/host/os-release", NULL);
}
/* Returns FLATPAK_FILESYSTEM_MODE_NONE if not visible */
FlatpakFilesystemMode
flatpak_exports_path_get_mode (FlatpakExports *exports,
const char *path)
{
guint n_keys;
g_autofree const char **keys = (const char **) g_hash_table_get_keys_as_array (exports->hash, &n_keys);
g_autofree char *canonical = NULL;
gboolean is_readonly = FALSE;
g_auto(GStrv) parts = NULL;
int i;
g_autoptr(GString) path_builder = g_string_new ("");
struct stat st;
g_qsort_with_data (keys, n_keys, sizeof (char *), (GCompareDataFunc) flatpak_strcmp0_ptr, NULL);
path = canonical = flatpak_canonicalize_filename (path);
parts = g_strsplit (path + 1, "/", -1);
/* A path is visible in the sandbox if no parent
* path element that is mapped in the sandbox is
* a symlink, and the final element is mapped.
* If any parent is a symlink we resolve that and
* continue with that instead.
*/
for (i = 0; parts[i] != NULL; i++)
{
g_string_append (path_builder, "/");
g_string_append (path_builder, parts[i]);
if (path_is_mapped (keys, n_keys, exports->hash, path_builder->str, &is_readonly))
{
if (lstat (path_builder->str, &st) != 0)
{
if (errno == ENOENT && parts[i + 1] == NULL && !is_readonly)
{
/* Last element was mapped but isn't there, this is
* OK (used for the save case) if we the parent is
* mapped and writable, as the app can then create
* the file here.
*/
break;
}
return FLATPAK_FILESYSTEM_MODE_NONE;
}
if (S_ISLNK (st.st_mode))
{
g_autofree char *resolved = flatpak_resolve_link (path_builder->str, NULL);
g_autoptr(GString) path2_builder = NULL;
int j;
if (resolved == NULL)
return FLATPAK_FILESYSTEM_MODE_NONE;
path2_builder = g_string_new (resolved);
for (j = i + 1; parts[j] != NULL; j++)
{
g_string_append (path2_builder, "/");
g_string_append (path2_builder, parts[j]);
}
return flatpak_exports_path_get_mode (exports, path2_builder->str);
}
}
else if (parts[i + 1] == NULL)
return FLATPAK_FILESYSTEM_MODE_NONE; /* Last part was not mapped */
}
if (is_readonly)
return FLATPAK_FILESYSTEM_MODE_READ_ONLY;
return FLATPAK_FILESYSTEM_MODE_READ_WRITE;
}
gboolean
flatpak_exports_path_is_visible (FlatpakExports *exports,
const char *path)
{
return flatpak_exports_path_get_mode (exports, path) > FLATPAK_FILESYSTEM_MODE_NONE;
}
static gboolean
never_export_as_symlink (const char *path)
{
/* Don't export /tmp as a symlink even if it is on the host, because
that will fail with the pre-existing directory we created for /tmp,
and anyway, it being a symlink is not useful in the sandbox */
if (strcmp (path, "/tmp") == 0)
return TRUE;
return FALSE;
}
static void
do_export_path (FlatpakExports *exports,
const char *path,
gint mode)
{
ExportedPath *old_ep = g_hash_table_lookup (exports->hash, path);
ExportedPath *ep;
g_return_if_fail (is_export_mode (mode));
ep = g_new0 (ExportedPath, 1);
ep->path = g_strdup (path);
if (old_ep != NULL)
ep->mode = MAX (old_ep->mode, mode);
else
ep->mode = mode;
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 (pipe2 (selfpipe, O_CLOEXEC) == -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
_exports_path_expose (FlatpakExports *exports,
int mode,
const char *path,
int level)
{
g_autofree char *canonical = NULL;
struct stat st;
struct statfs stfs;
char *slash;
int i;
glnx_autofd int o_path_fd = -1;
g_return_val_if_fail (is_export_mode (mode), FALSE);
if (level > 40) /* 40 is the current kernel ELOOP check */
{
g_debug ("Expose too deep, bail");
return FALSE;
}
if (!g_path_is_absolute (path))
{
g_debug ("Not exposing relative path %s", path);
return FALSE;
}
/* Check if it exists at all */
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 */
if (!(S_ISDIR (st.st_mode) ||
S_ISREG (st.st_mode) ||
S_ISLNK (st.st_mode) ||
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++)
{
/* Don't expose files in non-mounted dirs like /app or /usr, as
they are not the same as on the host, and we generally can't
create the parents for them anyway */
if (flatpak_has_path_prefix (path, dont_export_in[i]))
{
g_debug ("skipping export for path %s", path);
return FALSE;
}
}
for (i = 0; flatpak_abs_usrmerged_dirs[i] != NULL; i++)
{
/* Same as /usr, but for the directories that get merged into /usr */
if (flatpak_has_path_prefix (path, flatpak_abs_usrmerged_dirs[i]))
{
g_debug ("skipping export for path %s", path);
return FALSE;
}
}
/* Handle any symlinks prior to the target itself. This includes path itself,
because we expose the target of the symlink. */
slash = canonical;
do
{
slash = strchr (slash + 1, '/');
if (slash)
*slash = 0;
if (path_is_symlink (path) && !never_export_as_symlink (path))
{
g_autofree char *resolved = flatpak_resolve_link (path, NULL);
g_autofree char *new_target = NULL;
if (resolved)
{
if (slash)
new_target = g_build_filename (resolved, slash + 1, NULL);
else
new_target = g_strdup (resolved);
if (_exports_path_expose (exports, mode, new_target, level + 1))
{
do_export_path (exports, path, FAKE_MODE_SYMLINK);
return TRUE;
}
}
return FALSE;
}
if (slash)
*slash = '/';
}
while (slash != NULL);
do_export_path (exports, path, mode);
return TRUE;
}
void
flatpak_exports_add_path_expose (FlatpakExports *exports,
FlatpakFilesystemMode mode,
const char *path)
{
g_return_if_fail (mode > FLATPAK_FILESYSTEM_MODE_NONE);
g_return_if_fail (mode <= FLATPAK_FILESYSTEM_MODE_LAST);
_exports_path_expose (exports, mode, path, 0);
}
void
flatpak_exports_add_path_tmpfs (FlatpakExports *exports,
const char *path)
{
_exports_path_expose (exports, FAKE_MODE_TMPFS, path, 0);
}
void
flatpak_exports_add_path_expose_or_hide (FlatpakExports *exports,
FlatpakFilesystemMode mode,
const char *path)
{
g_return_if_fail (mode >= FLATPAK_FILESYSTEM_MODE_NONE);
g_return_if_fail (mode <= FLATPAK_FILESYSTEM_MODE_LAST);
if (mode == FLATPAK_FILESYSTEM_MODE_NONE)
flatpak_exports_add_path_tmpfs (exports, path);
else
flatpak_exports_add_path_expose (exports, mode, path);
}
void
flatpak_exports_add_path_dir (FlatpakExports *exports,
const char *path)
{
_exports_path_expose (exports, FAKE_MODE_DIR, path, 0);
}
void
flatpak_exports_add_host_etc_expose (FlatpakExports *exports,
FlatpakFilesystemMode mode)
{
g_return_if_fail (mode > FLATPAK_FILESYSTEM_MODE_NONE);
g_return_if_fail (mode <= FLATPAK_FILESYSTEM_MODE_LAST);
exports->host_etc = mode;
}
void
flatpak_exports_add_host_os_expose (FlatpakExports *exports,
FlatpakFilesystemMode mode)
{
g_return_if_fail (mode > FLATPAK_FILESYSTEM_MODE_NONE);
g_return_if_fail (mode <= FLATPAK_FILESYSTEM_MODE_LAST);
exports->host_os = mode;
}