Repository: flatpak/flatpak (C)

Issue:
bwrap: Can't mkdir parents for /run/host/usr: Permission denied
I've got the error message in the title when I tried to run two flatpaks:

- [org.gnome.Evince](https://github.com/flathub/org.gnome.Evince/issues/3)

- [org.libreoffice.LibreOffice](https://github.com/flathub/org.libreoffice.LibreOffice/issues/8)

I've installed them from flathub on a 64bit manjaro. Deleting their folders from ~/.var/app doesn't help.
[Bug]: Some --filesystem options such as --filesystem=/run make Flatpak apps fail to start
### Checklist

- [X] I agree to follow the [Code of Conduct](https://github.com/flatpak/flatpak/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/flatpak/flatpak/issues) for a bug that matches the one I want to file, without success.
- [X] If this is an issue with a particular app, I have tried filing it in the appropriate issue tracker for the app (e.g. under https://github.com/flathub/) and determined that it is an issue with Flatpak itself.
- [X] This issue is not a report of a security vulnerability (see [here](https://github.com/flatpak/flatpak/blob/main/SECURITY.md) if you need to report a security issue).

### Flatpak version

1.15.1

### What Linux distribution are you using?

Arch Linux

### Linux distribution version

N/A

### What architecture are you using?

x86_64

### How to reproduce

1. Grant all applications full access to a core runtime folder, e.g. with `flatpak override --user --filesystem /run/`
2. Try to launch any app (`flatpak run foo.bar.baz`)

### Expected Behavior

Instead of allowing the override, Flatpak should check for permission issues (just like it does when `--user` is not specified)

### Actual Behavior

Permissions are not checked, allowing this override to break all apps

### Additional Information

I've noticed this in a situation where an end user wanted to give one specific application access to a subfolder in `/run/` (`flatpak override --filesystem /run/someFolder foo.bar.baz`), but they've accidentally cut the command short, running `flatpak override --user --filesystem /run/` instead.

From an experienced user's standpoint, it's somewhat clear what has happened: `/run/` in its entirety is no longer sandboxed, but Flatpak tries to mirror some files from the actual system to `/run/host/`. Since we doesn't have write access to `/run/`, trying to create `/run/host/` fails.
Solving this is as simple as, for example, resetting all overrides (`flatpak override --user --reset`), but it's not easy for a non-technical user to come to this conclusion
[Bug]: cannot share all of /run with a Flatpak app
### Checklist

- [X] I agree to follow the [Code of Conduct](https://github.com/flatpak/flatpak/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/flatpak/flatpak/issues) for a bug that matches the one I want to file, without success.
- [X] If this is an issue with a particular app, I have tried filing it in the appropriate issue tracker for the app (e.g. under https://github.com/flathub/) and determined that it is an issue with Flatpak itself.
- [X] This issue is not a report of a security vulnerability (see [here](https://github.com/flatpak/flatpak/blob/main/SECURITY.md) if you need to report a security issue).

### Flatpak version

1.12.4

### What Linux distribution are you using?

Other (specify below)

### Linux distribution version

 Linux steamdeck 5.3.0-valve21.3-1-neptune

### What architecture are you using?

x86_64

### How to reproduce

Not sure exactly how this happened, as issue makes it difficult to debug, but I think steps were as follows
1. Install Flatseal
2. Use it to allow all flatpaks access to /run and some other folders (difficult to tell which because of issue)
3. Probably reboot
4. Try to launch any flatpak

### Expected Behavior

I expected flatpak to start and have access to my filesystem

### Actual Behavior

Flatpak did not launch with with terminal message
```
bwrap: Can't mkdir parents for /run/host/os-release: Permission denied
```

### Additional Information

Issue https://github.com/flatpak/flatpak/issues/1357 might be related, but I searched symlinks for / via command
```
sudo find -maxdepth 1 -type l -ls
```
and find only 5 old symlinks in /usr and in /var, nothing new or in /run


Discussion: I've made a new flatpak for myself today and it has the same issue: https://pastebin.com/A44KFTnW

The interesting thing is that I've these on two installations: one in virtualbox and one on my PC. In virtualbox these apps work. On the PC they don't. The installations are totally identical and I keep as much in sync as I can.
My theory is that `~/.local/share/flatpak` or its cache somewhere can get corrupted. When I've tried to rename a repo's alias I've uninstalled the apps and yet I couldn't reinstall them properly because it was missing the repo with the old name. I could only do the renaming after deleting `~/.local/share/flatpak` completely and reinstalling *everything*. This means the cache doesn't respect the removal which can cause issues like this.
@trialism Did you build flatpak yourself or use your distro's package?
@mwleeds I've installed it from manjaro's repo.
It also affects org.videolan.VLC now.
This issue is old, but I ran into the same problem. It seems to be caused by having a symlink in / that points to /, like `ln -s / /breakeverything`. I replicated this on my rather hacked-together Gentoo install as well as on an updated stock Fedora Workstation 29 VM (flatpak 1.0.5-2.fc29):

    // added flathub via discover, installed org.vim.Vim (though any app does it)
    [testuser@localhost ~]$ flatpak run org.vim.Vim --version | head -n1
    VIM - Vi IMproved 8.1 (2018 May 18, compiled Nov 12 2018 10:22:42)
    [testuser@localhost ~]$ sudo ln -s / /breakeverything
    [testuser@localhost ~]$ flatpak run org.vim.Vim --version | head -n1
    bwrap: Can't mkdir parents for /run/host/usr: Permission denied

If you give your regular user write rights to /run in the host, it appears to succeed at making /run/host inside of the host OS and bwrap will start complaining about being unable to create various "files" that are actually already-existing wayland/systemd/dbus/pulseaudio sockets inside of the host /run. Not sure what's happening there, all I know is that it seems to be breaking things in the host when it should be setting up the namespace.
Same problem on UnityHub. I've accidently ran 
"sudo flatpak run com.unity.UnityHub"

since then I can't run it without sudo.
Hmm, I guess we can have `flatpak run` refuse to work as root, but e.g. Kali Linux [used to](https://www.kali.org/news/kali-default-non-root-user/) run everything as root, so I wonder if we need a more nuanced check than that.
Can check if its ran under `sudo` (it sets a few env vars), but still allow it ran as root otherwise.
Why not just have warning till it's fixed. "Flatpaks shouldn't be ran as root. Run 'sudo flatpak my.app.id --allow-root' if you want to run it anyway".  
> Why not....

"Why not" questions are the worst. It alleviates any ownership, and shifts responsibility.... Why not just fix the whole thing?!

A better statement would be....

> I believe having a warning until it's fixed could be helpful. Something like "Flatpaks shouldn't be ran as root. Run 'sudo flatpak my.app.id --allow-root' if you want to run it anyway".

:wink: 
I've pushed a commit that handles the sudo case.
Regarding https://github.com/flatpak/flatpak/issues/1357#issuecomment-438861140 - there is an obvious workaround of deleting that symlink:

 * I had `/ROOT` symlink pointing to `/`;
 * I deleted it (`rm -v /ROOT`)
 * Flatpak now works like a charm!
> If you give your regular user write rights to /run in the host

In case it's not obvious: never do this. Write access to `/run` is root-equivalent.
#5213 should avoid this, by refusing to export (the target of) a symlink to `/`.

Simply not trying to mirror over files into `/run/host/` if `/run/` is not sandboxed is of course also an option, although I'd say that this wouldn't address the core issue
> Flatpak should check for permission issues (just like it does when --user is not specified)

Does it really? I think you might be misunderstanding the difference between `sudo flatpak override` and `flatpak override --user`.

When you use `flatpak override`, all you are doing is setting up a list of permissions that will be overridden when running an app later on. To use `flatpak override` without `--user` (which is equivalent to `flatpak override --system`), you have to be root, but only because you are doing something that will affect every user. Nothing is written to `/run` at this stage.

Later, when you use `flatpak run`, Flatpak combines all the permissions from several sources:

* defaults specified by the app maintainer (lowest precedence)
* system-wide all-apps overrides (`flatpak override --show --system`)
* system-wide per-app overrides (`flatpak override --show --system org.gnome.Recipes`)
* per-user all-apps overrides (`flatpak override --show --user`)
* per-user per-app overrides (`flatpak override --show --user org.gnome.Recipes`)
* `flatpak run` command-line options (highest precedence)

Specifying `--filesystem=/run` at any of those levels has exactly the same effect, unless it is overridden by something like `--nofilesystem=/run` at a higher-precedence level.

The effect of `--filesystem=/run` is that instead of `/run` being a tmpfs like it is by default, you are asking Flatpak to share the host system's `/run` with the app's sandbox. That can't work[1], because Flatpak needs to set up directories inside `/run` for its own purposes (mainly `/run/host` and `/run/flatpak`), and ordinary users don't have the ability to create mount points for those directories.

The only way a permission check affects this is that you are allowed to change configuration (and maybe break Flatpak) for yourself only, whereas root is allowed to change configuration (and maybe break Flatpak) for everyone.

Probably Flatpak should just reject `--filesystem=/run`, ideally with a warning, similar to the way it already ignores some specific directories like `--filesystem=/usr` and `--filesystem=/app`.

[1] Technically it can work if root runs `flatpak run`, but don't do that. `flatpak run` is not designed to be run as root.
> I think you might be misunderstanding the difference between `sudo flatpak override` and `flatpak override --user`.

I was indeed, thanks for clarifying! It was probably obvious from my post, but I don't use Flatpak myself. An end user of a program I work on encountered this issue, we managed to narrow it down to this, and I though I'd at least let you guys know

> Probably Flatpak should just reject `--filesystem=/run`, ideally with a warning, similar to the way it already ignores some specific directories like `--filesystem=/usr` and `--filesystem=/app`.

I wonder if there's a "list" somewhere in the codebase with all (normally non-writable/non-existent?) sandboxed directories. Going through that list & checking if the specified path would share this sandbox directory (and if it does, don't allow the override) seems more robust than adding directories one by one
> I wonder if there's a "list" somewhere in the codebase with all (normally non-writable/non-existent?) sandboxed directories.

Yes and no. The directories that are conceptually "owned" by the sandboxing framework are set up imperatively rather than declaratively (they really have to be, since each one has a different purpose and is set up in a different way), but there are also declarative and semi-declarative lists of paths that are different levels of special (`dont_export_in` and `never_export_as_symlink()` in `flatpak-exports.c`, and `dont_mount_in_root` in `flatpak-context.c`). `/run` is already in `dont_mount_in_root`, which excludes it from `--filesystem=host`.

Putting `/run` in `dont_export_in` (with `/usr` and `/app`) would be too strong: the purpose of `dont_export_in` is to not allow anything below that point either, like `--filesystem=/usr/share/blahblah` (which can't work even if we wanted to allow it, because `/usr` is read-only, so we would be unable to create the mount point). However, we do want you to be able to use something like `--filesystem=/run/blahblah/one-specific-socket`. What we should not be allowing is `--filesystem=/run` specifically, or anything below `--filesystem=/run/flatpak` or `--filesystem=/run/host`, or possibly others.

Maybe the ideal thing would be to expand the semantics of `dont_export_in` so that in addition to not allowing paths below those directories to be exported, we also don't allow paths above those directories to be exported? So that `dont_export_in[] = { ..., "/run/host", ... }` would imply not allowing `/run/host/foo` (already true) or `/run` (new), but would still allow `/run/foo`, where `foo` can be anything?
#5213 avoids this by refusing to share all of `/run` with the app, with a warning message.

> Maybe the ideal thing would be to expand the semantics of `dont_export_in` so that in addition to not allowing paths below those directories to be exported, we also don't allow paths above those directories to be exported?

That's (a large part of) what I did in #5213.

> allow all flatpaks access to /run

That can't work. Flatpak relies on the `/run` inside the container being a tmpfs under its control so that it can create `/run/host`, `/run/flatpak` and potentially others, which it will be unable to do if you change `/run` to be shared from the host system.

Probably Flatpak should ignore `--filesystem=/run`, ideally with a warning, the same way it already ignores `--filesystem=/usr`.

Instead of sharing `/run` with the host, share individual sockets in `/run` with the host (and try to do that as minimally as possible, because each socket you share is an extra route through which a malicious or compromised app might escape from the sandbox).
> Use it to allow all flatpaks access to /run and some other folders (difficult to tell which because of issue)

Please include the full list of overrides in issue reports, particularly if you suspect the overrides are part of the reason for failure. You can see the overrides with:

1. `flatpak override --show --system`
2. `flatpak override --show --system APP`
3. `flatpak override --show --user`
4. `flatpak override --show --user APP`

replacing *APP* with an app ID like `org.gnome.Recipes`. The first and third commands show overrides that apply to all apps, the second and fourth commands show overrides that are specific to one app. The first and second show overrides that apply to all users, the third and fourth show overrides that apply to you specifically.

To remove overrides that are causing trouble, either use Flatseal, or you can remove *all* overrides with:

1. `sudo flatpak override --reset --system`
2. `sudo flatpak override --reset --system APP`
3. `flatpak override --reset --user`
4. `flatpak override --reset --user APP`

Similar to the `--show` commands, the first and third commands remove overrides that apply to all apps, while the second and fourth commands remove overrides that are specific to one app. The first and second remove overrides that apply to all users, the third and fourth remove overrides that apply to you specifically. To remove *all* overrides, you would potentially need all four of these commands.
yes, there were /run in overrides, and after I removed them issue resolved
#5213 avoids this by refusing to share all of `/run` with the app, with a warning message.



Fix (patch):
diff --git a/common/flatpak-context.c b/common/flatpak-context.c
index 9e028fb5d0..ddc891dba6 100644
--- a/common/flatpak-context.c
+++ b/common/flatpak-context.c
@@ -2435,10 +2435,65 @@ flatpak_context_make_sandboxed (FlatpakContext *context)
 }
 
 const char *dont_mount_in_root[] = {
-  ".", "..", "lib", "lib32", "lib64", "bin", "sbin", "usr", "boot", "efi",
-  "root", "tmp", "etc", "app", "run", "proc", "sys", "dev", "var", NULL
+  ".",
+  "..",
+  "app",
+  "bin",
+  "boot",
+  "dev",
+  "efi",
+  "etc",
+  "lib",
+  "lib32",
+  "lib64",
+  "proc",
+  "root",
+  "run",
+  "sbin",
+  "sys",
+  "tmp",
+  "usr",
+  "var",
+  NULL
 };
 
+static void
+log_cannot_export_error (FlatpakFilesystemMode  mode,
+                         const char            *path,
+                         const GError          *error)
+{
+  GLogLevelFlags level = G_LOG_LEVEL_MESSAGE;
+
+  /* By default we don't show a log message if the reason we are not sharing
+   * something with the sandbox is simply "it doesn't exist" (or something
+   * very close): otherwise it would be very noisy to launch apps that
+   * opportunistically share things they might benefit from, like Steam
+   * having access to $XDG_RUNTIME_DIR/app/com.discordapp.Discord if it
+   * happens to exist. */
+  if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+    level = G_LOG_LEVEL_INFO;
+  /* Some callers specifically suppress warnings for particular errors
+   * by setting this code. */
+  else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED))
+    level = G_LOG_LEVEL_INFO;
+
+  switch (mode)
+    {
+      case FLATPAK_FILESYSTEM_MODE_NONE:
+        g_log (G_LOG_DOMAIN, level, _("Not replacing \"%s\" with tmpfs: %s"),
+               path, error->message);
+        break;
+
+      case FLATPAK_FILESYSTEM_MODE_CREATE:
+      case FLATPAK_FILESYSTEM_MODE_READ_ONLY:
+      case FLATPAK_FILESYSTEM_MODE_READ_WRITE:
+        g_log (G_LOG_DOMAIN, level,
+               _("Not sharing \"%s\" with sandbox: %s"),
+               path, error->message);
+        break;
+    }
+}
+
 static void
 flatpak_context_export (FlatpakContext *context,
                         FlatpakExports *exports,
@@ -2453,6 +2508,7 @@ flatpak_context_export (FlatpakContext *context,
   FlatpakFilesystemMode fs_mode, os_mode, etc_mode, home_mode;
   GHashTableIter iter;
   gpointer key, value;
+  g_autoptr(GError) local_error = NULL;
 
   if (xdg_dirs_conf_out != NULL)
     xdg_dirs_conf = g_string_new ("");
@@ -2478,11 +2534,30 @@ flatpak_context_export (FlatpakContext *context,
                 continue;
 
               path = g_build_filename ("/", dirent->d_name, NULL);
-              flatpak_exports_add_path_expose (exports, fs_mode, path);
+
+              if (!flatpak_exports_add_path_expose (exports, fs_mode, path, &local_error))
+                {
+                  /* Failure to share something like /lib32 because it's
+                   * actually a symlink to /usr/lib32 is less of a problem
+                   * here than it would be for an explicit
+                   * --filesystem=/lib32, so the warning that would normally
+                   * be produced in that situation is downgraded to a
+                   * debug message. */
+                  if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE))
+                    local_error->code = G_IO_ERROR_FAILED_HANDLED;
+
+                  log_cannot_export_error (fs_mode, path, local_error);
+                  g_clear_error (&local_error);
+                }
             }
           closedir (dir);
         }
-      flatpak_exports_add_path_expose (exports, fs_mode, "/run/media");
+
+      if (!flatpak_exports_add_path_expose (exports, fs_mode, "/run/media", &local_error))
+        {
+          log_cannot_export_error (fs_mode, "/run/media", local_error);
+          g_clear_error (&local_error);
+        }
     }
 
   os_mode = MAX (GPOINTER_TO_INT (g_hash_table_lookup (context->filesystems, "host-os")),
@@ -2503,7 +2578,16 @@ flatpak_context_export (FlatpakContext *context,
       g_info ("Allowing homedir access");
       home_access = TRUE;
 
-      flatpak_exports_add_path_expose (exports, MAX (home_mode, fs_mode), g_get_home_dir ());
+      if (!flatpak_exports_add_path_expose (exports, MAX (home_mode, fs_mode), g_get_home_dir (), &local_error))
+        {
+          /* Even if the error is one that we would normally silence, like
+           * the path not existing, it seems reasonable to make more of a fuss
+           * about the home directory not existing or otherwise being unusable,
+           * so this is intentionally not using cannot_export() */
+          g_warning (_("Not allowing home directory access: %s"),
+                     local_error->message);
+          g_clear_error (&local_error);
+        }
     }
 
   g_hash_table_iter_init (&iter, context->filesystems);
@@ -2553,7 +2637,11 @@ flatpak_context_export (FlatpakContext *context,
                 g_string_append_printf (xdg_dirs_conf, "%s=\"%s\"\n",
                                         config_key, path);
 
-              flatpak_exports_add_path_expose_or_hide (exports, mode, subpath);
+              if (!flatpak_exports_add_path_expose_or_hide (exports, mode, subpath, &local_error))
+                {
+                  log_cannot_export_error (mode, subpath, local_error);
+                  g_clear_error (&local_error);
+                }
             }
         }
       else if (g_str_has_prefix (filesystem, "~/"))
@@ -2568,8 +2656,11 @@ flatpak_context_export (FlatpakContext *context,
                 g_info ("Unable to create directory %s", path);
             }
 
-          if (g_file_test (path, G_FILE_TEST_EXISTS))
-            flatpak_exports_add_path_expose_or_hide (exports, mode, path);
+          if (!flatpak_exports_add_path_expose_or_hide (exports, mode, path, &local_error))
+            {
+              log_cannot_export_error (mode, path, local_error);
+              g_clear_error (&local_error);
+            }
         }
       else if (g_str_has_prefix (filesystem, "/"))
         {
@@ -2579,8 +2670,11 @@ flatpak_context_export (FlatpakContext *context,
                 g_info ("Unable to create directory %s", filesystem);
             }
 
-          if (g_file_test (filesystem, G_FILE_TEST_EXISTS))
-            flatpak_exports_add_path_expose_or_hide (exports, mode, filesystem);
+          if (!flatpak_exports_add_path_expose_or_hide (exports, mode, filesystem, &local_error))
+            {
+              log_cannot_export_error (mode, filesystem, local_error);
+              g_clear_error (&local_error);
+            }
         }
       else
         {
@@ -2593,18 +2687,42 @@ flatpak_context_export (FlatpakContext *context,
       g_autoptr(GFile) apps_dir = g_file_get_parent (app_id_dir);
       int i;
       /* Hide the .var/app dir by default (unless explicitly made visible) */
-      flatpak_exports_add_path_tmpfs (exports, flatpak_file_get_path_cached (apps_dir));
+      if (!flatpak_exports_add_path_tmpfs (exports,
+                                           flatpak_file_get_path_cached (apps_dir),
+                                           &local_error))
+        {
+          log_cannot_export_error (FLATPAK_FILESYSTEM_MODE_NONE,
+                                   flatpak_file_get_path_cached (apps_dir),
+                                   local_error);
+          g_clear_error (&local_error);
+        }
+
       /* But let the app write to the per-app dir in it */
-      flatpak_exports_add_path_expose (exports, FLATPAK_FILESYSTEM_MODE_READ_WRITE,
-                                       flatpak_file_get_path_cached (app_id_dir));
+      if (!flatpak_exports_add_path_expose (exports, FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                            flatpak_file_get_path_cached (app_id_dir),
+                                            &local_error))
+        {
+          log_cannot_export_error (FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                   flatpak_file_get_path_cached (apps_dir),
+                                   local_error);
+          g_clear_error (&local_error);
+        }
 
       if (extra_app_id_dirs != NULL)
         {
           for (i = 0; i < extra_app_id_dirs->len; i++)
             {
               GFile *extra_app_id_dir = g_ptr_array_index (extra_app_id_dirs, i);
-              flatpak_exports_add_path_expose (exports, FLATPAK_FILESYSTEM_MODE_READ_WRITE,
-                                               flatpak_file_get_path_cached (extra_app_id_dir));
+              if (!flatpak_exports_add_path_expose (exports,
+                                                    FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                                    flatpak_file_get_path_cached (extra_app_id_dir),
+                                                    &local_error))
+                {
+                  log_cannot_export_error (FLATPAK_FILESYSTEM_MODE_READ_WRITE,
+                                           flatpak_file_get_path_cached (extra_app_id_dir),
+                                           local_error);
+                  g_clear_error (&local_error);
+                }
             }
         }
     }
@@ -2668,13 +2786,27 @@ flatpak_context_get_exports_full (FlatpakContext *context,
   if (include_default_dirs)
     {
       g_autoptr(GFile) user_flatpak_dir = NULL;
+      g_autoptr(GError) local_error = NULL;
 
       /* Hide the flatpak dir by default (unless explicitly made visible) */
       user_flatpak_dir = flatpak_get_user_base_dir_location ();
-      flatpak_exports_add_path_tmpfs (exports, flatpak_file_get_path_cached (user_flatpak_dir));
+      if (!flatpak_exports_add_path_tmpfs (exports,
+                                           flatpak_file_get_path_cached (user_flatpak_dir),
+                                           &local_error))
+        {
+          log_cannot_export_error (FLATPAK_FILESYSTEM_MODE_NONE,
+                                   flatpak_file_get_path_cached (user_flatpak_dir),
+                                   local_error);
+          g_clear_error (&local_error);
+        }
 
       /* Ensure we always have a homedir */
-      flatpak_exports_add_path_dir (exports, g_get_home_dir ());
+      if (!flatpak_exports_add_path_dir (exports, g_get_home_dir (), &local_error))
+        {
+          g_warning (_("Unable to provide a temporary home directory in the sandbox: %s"),
+                     local_error->message);
+          g_clear_error (&local_error);
+        }
     }
 
   return g_steal_pointer (&exports);
diff --git a/common/flatpak-exports-private.h b/common/flatpak-exports-private.h
index 90308910c1..3a43ecb546 100644
--- a/common/flatpak-exports-private.h
+++ b/common/flatpak-exports-private.h
@@ -43,16 +43,20 @@ void flatpak_exports_add_host_etc_expose (FlatpakExports       *exports,
                                           FlatpakFilesystemMode mode);
 void flatpak_exports_add_host_os_expose (FlatpakExports       *exports,
                                          FlatpakFilesystemMode mode);
-void flatpak_exports_add_path_expose (FlatpakExports       *exports,
-                                      FlatpakFilesystemMode mode,
-                                      const char           *path);
-void flatpak_exports_add_path_tmpfs (FlatpakExports *exports,
-                                     const char     *path);
-void flatpak_exports_add_path_expose_or_hide (FlatpakExports       *exports,
-                                              FlatpakFilesystemMode mode,
-                                              const char           *path);
-void flatpak_exports_add_path_dir (FlatpakExports *exports,
-                                   const char     *path);
+gboolean flatpak_exports_add_path_expose (FlatpakExports         *exports,
+                                          FlatpakFilesystemMode   mode,
+                                          const char             *path,
+                                          GError                **error);
+gboolean flatpak_exports_add_path_tmpfs (FlatpakExports  *exports,
+                                         const char      *path,
+                                         GError         **error);
+gboolean flatpak_exports_add_path_expose_or_hide (FlatpakExports         *exports,
+                                                  FlatpakFilesystemMode   mode,
+                                                  const char             *path,
+                                                  GError                **error);
+gboolean flatpak_exports_add_path_dir (FlatpakExports  *exports,
+                                       const char      *path,
+                                       GError         **error);
 
 gboolean flatpak_exports_path_is_visible (FlatpakExports *exports,
                                           const char     *path);
diff --git a/common/flatpak-exports.c b/common/flatpak-exports.c
index 15acc5b89b..ce7b28ec1d 100644
--- a/common/flatpak-exports.c
+++ b/common/flatpak-exports.c
@@ -52,7 +52,15 @@
    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
+  "/.flatpak-info",
+  "/app",
+  "/dev",
+  "/etc",
+  "/proc",
+  "/run/flatpak",
+  "/run/host",
+  "/usr",
+  NULL
 };
 
 static char *
@@ -861,12 +869,18 @@ check_if_autofs_works (FlatpakExports *exports,
   return TRUE;
 }
 
-/* We use level to avoid infinite recursion */
+/* We use level to avoid infinite recursion.
+ *
+ * Note that some of the errors produced by this function are "real errors"
+ * and should show up as a user-visible warning, but others are relatively
+ * uninteresting, and in general none are actually fatal: we prefer to
+ * continue with fewer paths exposed rather than failing to run. */
 static gboolean
 _exports_path_expose (FlatpakExports *exports,
                       int             mode,
                       const char     *path,
-                      int             level)
+                      int             level,
+                      GError        **error)
 {
   g_autofree char *canonical = NULL;
   struct stat st;
@@ -881,13 +895,15 @@ _exports_path_expose (FlatpakExports *exports,
 
   if (level > 40) /* 40 is the current kernel ELOOP check */
     {
-      g_info ("Expose too deep, bail");
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_TOO_MANY_LINKS,
+                   "%s", g_strerror (ELOOP));
       return FALSE;
     }
 
   if (!g_path_is_absolute (path))
     {
-      g_info ("Not exposing relative path %s", path);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME,
+                   _("An absolute path is required"));
       return FALSE;
     }
 
@@ -896,34 +912,38 @@ _exports_path_expose (FlatpakExports *exports,
 
   if (o_path_fd == -1)
     {
-      g_info ("Unable to open path %s to %s: %s",
-              path, export_mode_to_verb (mode), g_strerror (errno));
+      int saved_errno = errno;
+
+      /* Intentionally using G_IO_ERROR_NOT_FOUND even if errno is
+       * something different, so callers can suppress the warning in this
+       * relatively likely and uninteresting case: we don't particularly
+       * care whether this is happening as a result of ENOENT or EACCES
+       * or any other reason. */
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                   _("Unable to open path \"%s\": %s"),
+                   path, g_strerror (saved_errno));
       return FALSE;
     }
 
   if (fstat (o_path_fd, &st) != 0)
-    {
-      g_info ("Unable to get file type of %s: %s", path, g_strerror (errno));
-      return FALSE;
-    }
+    return glnx_throw (error,
+                       _("Unable to get file type of \"%s\": %s"),
+                       path, g_strerror (errno));
 
   /* 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)))
-    {
-      g_info ("%s has unsupported file type 0o%o", path, st.st_mode & S_IFMT);
-      return FALSE;
-    }
+    return glnx_throw (error,
+                       _("File \"%s\" has unsupported type 0o%o"),
+                       path, st.st_mode & S_IFMT);
 
   /* O_PATH + fstatfs is the magic that we need to statfs without automounting the target */
   if (fstatfs (o_path_fd, &stfs) != 0)
-    {
-      g_info ("Unable to get filesystem information for %s: %s",
-              path, g_strerror (errno));
-      return FALSE;
-    }
+    return glnx_throw (error,
+                       _("Unable to get filesystem information for \"%s\": %s"),
+                       path, g_strerror (errno));
 
   if (stfs.f_type == AUTOFS_SUPER_MAGIC ||
       (G_UNLIKELY (exports->test_flags & FLATPAK_EXPORTS_TEST_FLAGS_AUTOFS) &&
@@ -931,7 +951,8 @@ _exports_path_expose (FlatpakExports *exports,
     {
       if (!check_if_autofs_works (exports, path))
         {
-          g_info ("ignoring blocking autofs path %s", path);
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
+                       _("Ignoring blocking autofs path \"%s\""), path);
           return FALSE;
         }
     }
@@ -946,17 +967,35 @@ _exports_path_expose (FlatpakExports *exports,
          create the parents for them anyway */
       if (flatpak_has_path_prefix (path, dont_export_in[i]))
         {
-          g_info ("skipping export for path %s in unsupported prefix", path);
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE,
+                       _("Path \"%s\" is reserved by Flatpak"),
+                       dont_export_in[i]);
+          return FALSE;
+        }
+
+      /* Also don't expose directories that are a parent of a directory
+       * that is "owned" by the sandboxing framework. For example, because
+       * Flatpak controls /run/host and /run/flatpak, we cannot allow
+       * --filesystem=/run, which would prevent us from creating the
+       * contents of /run/host and /run/flatpak. */
+      if (flatpak_has_path_prefix (dont_export_in[i], path))
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE,
+                       _("Path \"%s\" is reserved by Flatpak"),
+                       dont_export_in[i]);
           return FALSE;
         }
     }
 
   for (i = 0; flatpak_abs_usrmerged_dirs[i] != NULL; i++)
     {
-      /* Same as /usr, but for the directories that get merged into /usr */
+      /* Same as /usr, but for the directories that get merged into /usr.
+       * Keep the translatable string here the same as the one above */
       if (flatpak_has_path_prefix (path, flatpak_abs_usrmerged_dirs[i]))
         {
-          g_info ("skipping export for path %s in a /usr-merged directory", path);
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTABLE_FILE,
+                       _("Path \"%s\" is reserved by Flatpak"),
+                       flatpak_abs_usrmerged_dirs[i]);
           return FALSE;
         }
     }
@@ -980,8 +1019,8 @@ _exports_path_expose (FlatpakExports *exports,
         }
       else
         {
-          g_autoptr(GError) error = NULL;
-          g_autofree char *resolved = flatpak_exports_resolve_link_in_host (exports, path, &error);
+          g_autoptr(GError) local_error = NULL;
+          g_autofree char *resolved = flatpak_exports_resolve_link_in_host (exports, path, &local_error);
           g_autofree char *new_target = NULL;
 
           if (resolved)
@@ -995,7 +1034,7 @@ _exports_path_expose (FlatpakExports *exports,
 
               g_debug ("Trying to export the target instead: %s", new_target);
 
-              if (_exports_path_expose (exports, mode, new_target, level + 1))
+              if (_exports_path_expose (exports, mode, new_target, level + 1, &local_error))
                 {
                   do_export_path (exports, path, FAKE_MODE_SYMLINK);
                   return TRUE;
@@ -1003,12 +1042,14 @@ _exports_path_expose (FlatpakExports *exports,
 
               g_debug ("Could not export target %s, so ignoring %s",
                        new_target, path);
+              g_propagate_error (error, g_steal_pointer (&local_error));
               return FALSE;
             }
           else
             {
-              g_debug ("%s is a symlink but we were unable to resolve it: %s",
-                       path, error->message);
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                           _("Unable to resolve symbolic link \"%s\": %s"),
+                           path, local_error->message);
               return FALSE;
             }
         }
@@ -1022,42 +1063,46 @@ _exports_path_expose (FlatpakExports *exports,
   return TRUE;
 }
 
-void
-flatpak_exports_add_path_expose (FlatpakExports       *exports,
-                                 FlatpakFilesystemMode mode,
-                                 const char           *path)
+gboolean
+flatpak_exports_add_path_expose (FlatpakExports         *exports,
+                                 FlatpakFilesystemMode   mode,
+                                 const char             *path,
+                                 GError                **error)
 {
-  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);
+  g_return_val_if_fail (mode > FLATPAK_FILESYSTEM_MODE_NONE, FALSE);
+  g_return_val_if_fail (mode <= FLATPAK_FILESYSTEM_MODE_LAST, FALSE);
+  return _exports_path_expose (exports, mode, path, 0, error);
 }
 
-void
-flatpak_exports_add_path_tmpfs (FlatpakExports *exports,
-                                const char     *path)
+gboolean
+flatpak_exports_add_path_tmpfs (FlatpakExports  *exports,
+                                const char      *path,
+                                GError         **error)
 {
-  _exports_path_expose (exports, FAKE_MODE_TMPFS, path, 0);
+  return _exports_path_expose (exports, FAKE_MODE_TMPFS, path, 0, error);
 }
 
-void
-flatpak_exports_add_path_expose_or_hide (FlatpakExports       *exports,
-                                         FlatpakFilesystemMode mode,
-                                         const char           *path)
+gboolean
+flatpak_exports_add_path_expose_or_hide (FlatpakExports        *exports,
+                                         FlatpakFilesystemMode  mode,
+                                         const char            *path,
+                                         GError               **error)
 {
-  g_return_if_fail (mode >= FLATPAK_FILESYSTEM_MODE_NONE);
-  g_return_if_fail (mode <= FLATPAK_FILESYSTEM_MODE_LAST);
+  g_return_val_if_fail (mode >= FLATPAK_FILESYSTEM_MODE_NONE, FALSE);
+  g_return_val_if_fail (mode <= FLATPAK_FILESYSTEM_MODE_LAST, FALSE);
 
   if (mode == FLATPAK_FILESYSTEM_MODE_NONE)
-    flatpak_exports_add_path_tmpfs (exports, path);
+    return flatpak_exports_add_path_tmpfs (exports, path, error);
   else
-    flatpak_exports_add_path_expose (exports, mode, path);
+    return flatpak_exports_add_path_expose (exports, mode, path, error);
 }
 
-void
-flatpak_exports_add_path_dir (FlatpakExports *exports,
-                              const char     *path)
+gboolean
+flatpak_exports_add_path_dir (FlatpakExports  *exports,
+                              const char      *path,
+                              GError         **error)
 {
-  _exports_path_expose (exports, FAKE_MODE_DIR, path, 0);
+  return _exports_path_expose (exports, FAKE_MODE_DIR, path, 0, error);
 }
 
 void