diff --git a/subprojects/bubblewrap/.github/workflows/check.yml b/subprojects/bubblewrap/.github/workflows/check.yml
index be21f8a8bb4f80343c203b407606b563447253bd..7bc034b554b40609f361fd90a0b609b6cf1874fb 100644
--- a/subprojects/bubblewrap/.github/workflows/check.yml
+++ b/subprojects/bubblewrap/.github/workflows/check.yml
@@ -9,72 +9,12 @@ on:
     - main
 
 jobs:
-  check:
-    name: Build with Autotools and gcc, and test
-    runs-on: ubuntu-latest
-    steps:
-    - name: Check out
-      uses: actions/checkout@v1
-    - name: Install build-dependencies
-      run: sudo ./ci/builddeps.sh
-    - name: Create logs dir
-      run: mkdir test-logs
-    - name: autogen.sh
-      run: NOCONFIGURE=1 ./autogen.sh
-    - name: configure
-      run: |
-        mkdir _build
-        pushd _build
-        ../configure \
-          --enable-man \
-          --enable-selinux \
-          ${NULL+}
-        popd
-      env:
-        CFLAGS: >-
-          -O2
-          -Wp,-D_FORTIFY_SOURCE=2
-          -fsanitize=address
-          -fsanitize=undefined
-    - name: make
-      run: make -C _build -j $(getconf _NPROCESSORS_ONLN) V=1
-    - name: smoke-test
-      run: |
-        set -x
-        ./_build/bwrap --bind / / --tmpfs /tmp true
-      env:
-        ASAN_OPTIONS: detect_leaks=0
-    - name: check
-      run: |
-        make -C _build -j $(getconf _NPROCESSORS_ONLN) check VERBOSE=1 BWRAP_MUST_WORK=1
-      env:
-        ASAN_OPTIONS: detect_leaks=0
-    - name: Collect overall test logs on failure
-      if: failure()
-      run: mv _build/test-suite.log test-logs/ || true
-    - name: Collect individual test logs on cancel
-      if: failure() || cancelled()
-      run: mv _build/tests/*.log test-logs/ || true
-    - name: Upload test logs
-      uses: actions/upload-artifact@v1
-      if: failure() || cancelled()
-      with:
-        name: test logs
-        path: test-logs
-    - name: install
-      run: |
-        make -C _build install DESTDIR="$(pwd)/DESTDIR"
-        ( cd DESTDIR && find -ls )
-    - name: distcheck
-      run: |
-        make -C _build -j $(getconf _NPROCESSORS_ONLN) distcheck VERBOSE=1 BWRAP_MUST_WORK=1
-
   meson:
     name: Build with Meson and gcc, and test
     runs-on: ubuntu-latest
     steps:
     - name: Check out
-      uses: actions/checkout@v1
+      uses: actions/checkout@v4
     - name: Install build-dependencies
       run: sudo ./ci/builddeps.sh
     - name: Create logs dir
@@ -129,7 +69,7 @@ jobs:
         test ! -e DESTDIR-as-subproject/usr/local/libexec/bwrap
         tests/use-as-subproject/assert-correct-rpath.py DESTDIR-as-subproject/usr/local/libexec/not-flatpak-bwrap
     - name: Upload test logs
-      uses: actions/upload-artifact@v1
+      uses: actions/upload-artifact@v4
       if: failure() || cancelled()
       with:
         name: test logs
@@ -149,19 +89,15 @@ jobs:
       with:
         languages: ${{ matrix.language }}
     - name: Check out
-      uses: actions/checkout@v1
+      uses: actions/checkout@v4
     - name: Install build-dependencies
       run: sudo ./ci/builddeps.sh --clang
-    - name: autogen.sh
-      run: NOCONFIGURE=1 ./autogen.sh
-    - name: configure
-      run: ./configure --enable-selinux
+    - run: meson build -Dselinux=enabled
       env:
         CC: clang
         CFLAGS: >-
           -O2
           -Werror=unused-variable
-    - name: make
-      run: make -j $(getconf _NPROCESSORS_ONLN) V=1
+    - run: meson compile -C build
     - name: CodeQL analysis
       uses: github/codeql-action/analyze@v2
diff --git a/subprojects/bubblewrap/Makefile-bwrap.am b/subprojects/bubblewrap/Makefile-bwrap.am
deleted file mode 100644
index aa97bd17ac4b0c7a6f555d6f5e5a06da57c30d5b..0000000000000000000000000000000000000000
--- a/subprojects/bubblewrap/Makefile-bwrap.am
+++ /dev/null
@@ -1,12 +0,0 @@
-bwrap_SOURCES = \
-	$(bwrap_srcpath)/bubblewrap.c \
-	$(bwrap_srcpath)/bind-mount.h \
-	$(bwrap_srcpath)/bind-mount.c \
-	$(bwrap_srcpath)/network.h \
-	$(bwrap_srcpath)/network.c \
-	$(bwrap_srcpath)/utils.h \
-	$(bwrap_srcpath)/utils.c \
-	$(NULL)
-
-bwrap_CFLAGS = $(AM_CFLAGS)
-bwrap_LDADD = $(SELINUX_LIBS)
diff --git a/subprojects/bubblewrap/Makefile-docs.am b/subprojects/bubblewrap/Makefile-docs.am
deleted file mode 100644
index 4c1f3c5ccf5ca1cfb745766a7944afa41c176a23..0000000000000000000000000000000000000000
--- a/subprojects/bubblewrap/Makefile-docs.am
+++ /dev/null
@@ -1,18 +0,0 @@
-XSLTPROC = xsltproc
-
-XSLTPROC_FLAGS = \
-	--nonet \
-	--stringparam man.output.quietly 1 \
-	--stringparam funcsynopsis.style ansi \
-	--stringparam man.th.extra1.suppress 1 \
-	--stringparam man.authors.section.enabled 0 \
-	--stringparam man.copyright.section.enabled 0
-
-.xml.1:
-	$(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
-
-if ENABLE_MAN
-man_MANS = bwrap.1
-CLEANFILES += $(man_MANS)
-endif
-EXTRA_DIST += bwrap.xml
diff --git a/subprojects/bubblewrap/Makefile.am b/subprojects/bubblewrap/Makefile.am
deleted file mode 100644
index 81a160aa76c51fb05f3285d96f9ebad3b0322f45..0000000000000000000000000000000000000000
--- a/subprojects/bubblewrap/Makefile.am
+++ /dev/null
@@ -1,102 +0,0 @@
-AM_CFLAGS = $(WARN_CFLAGS)
-CLEANFILES =
-EXTRA_DIST = \
-	.dir-locals.el \
-	.editorconfig \
-	README.md \
-	autogen.sh \
-	completions/bash/meson.build \
-	completions/meson.build \
-	completions/zsh/meson.build \
-	demos/bubblewrap-shell.sh \
-	demos/flatpak-run.sh \
-	demos/flatpak.bpf \
-	demos/userns-block-fd.py \
-	meson.build \
-	meson_options.txt \
-	packaging/bubblewrap.spec \
-	tests/meson.build \
-	tests/use-as-subproject/README \
-	tests/use-as-subproject/config.h \
-	tests/use-as-subproject/dummy-config.h.in \
-	tests/use-as-subproject/meson.build \
-	uncrustify.cfg \
-	uncrustify.sh \
-	$(NULL)
-
-GITIGNOREFILES = build-aux/ gtk-doc.make config.h.in aclocal.m4
-
-bin_PROGRAMS = bwrap
-
-bwrap_srcpath := $(srcdir)
-include Makefile-bwrap.am
-
-install-exec-hook:
-if PRIV_MODE_SETUID
-	$(SUDO_BIN) chown root $(DESTDIR)$(bindir)/bwrap
-	$(SUDO_BIN) chmod u+s $(DESTDIR)$(bindir)/bwrap
-endif
-
-test_programs = \
-	tests/test-utils \
-	$(NULL)
-test_scripts = \
-	tests/test-run.sh \
-	tests/test-seccomp.py \
-	tests/test-specifying-userns.sh \
-	tests/test-specifying-pidns.sh \
-	$(NULL)
-test_extra_programs = \
-	test-bwrap \
-	tests/try-syscall \
-	$(NULL)
-
-test-bwrap$(EXEEXT): bwrap
-	rm -rf test-bwrap
-	cp bwrap test-bwrap
-	chmod 0755 test-bwrap
-if PRIV_MODE_SETUID
-	$(SUDO_BIN) chown root test-bwrap
-	$(SUDO_BIN) chmod u+s test-bwrap
-endif
-
-tests_test_utils_SOURCES = \
-	tests/test-utils.c \
-	utils.h \
-	utils.c \
-	$(NULL)
-tests_test_utils_LDADD = $(SELINUX_LIBS)
-
-test_bwrap_SOURCES=
-
-include Makefile-docs.am
-
-LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
-LOG_COMPILER =
-TESTS_ENVIRONMENT = \
-	BWRAP=$(abs_top_builddir)/test-bwrap \
-	G_TEST_BUILDDIR=$(abs_top_builddir) \
-	G_TEST_SRCDIR=$(abs_top_srcdir) \
-	$(NULL)
-check_PROGRAMS = $(test_programs) $(test_extra_programs)
-TESTS = $(test_programs) $(test_scripts)
-
-EXTRA_DIST += $(test_scripts)
-EXTRA_DIST += tests/libtest-core.sh
-EXTRA_DIST += tests/libtest.sh
-
-if ENABLE_BASH_COMPLETION
-bashcompletiondir = $(BASH_COMPLETION_DIR)
-dist_bashcompletion_DATA = completions/bash/bwrap
-endif
-
-if ENABLE_ZSH_COMPLETION
-zshcompletiondir = $(ZSH_COMPLETION_DIR)
-dist_zshcompletion_DATA = completions/zsh/_bwrap
-endif
-
--include $(top_srcdir)/git.mk
-
-AM_DISTCHECK_CONFIGURE_FLAGS =		\
-	--with-bash-completion-dir="\$(datadir)"/bash-completion/ \
-	$(NULL)
diff --git a/subprojects/bubblewrap/NEWS.md b/subprojects/bubblewrap/NEWS.md
new file mode 100644
index 0000000000000000000000000000000000000000..da232c4bd77846e040411949cd24a96a1e85952c
--- /dev/null
+++ b/subprojects/bubblewrap/NEWS.md
@@ -0,0 +1,51 @@
+bubblewrap 0.11.0
+=================
+
+Released: 2024-10-30
+
+Dependencies:
+
+  * Remove the Autotools build system. Meson ≥ 0.49.0 is now required
+    at build-time. (#625, Hugo Osvaldo Barrera)
+
+  * For users of bash-completion, bash-completion ≥ 2.10 is recommended.
+    With older bash-completion, bubblewrap might install completions
+    outside its `${prefix}` unless overridden with `-Dbash_completion_dir=…`.
+
+Enhancements:
+
+  * New `--overlay`, `--tmp-overlay`, `--ro-overlay` and `--overlay-src`
+    options allow creation of overlay mounts.
+    This feature is not available when bubblewrap is installed setuid.
+    (#412, #663; Ryan Hendrickson, William Manley, Simon McVittie)
+
+  * New `--level-prefix` option produces output that can be parsed by
+    tools like `logger --prio-prefix` and `systemd-cat --level-prefix=1`
+    (#646, Simon McVittie)
+
+Bug fixes:
+
+  * Handle `EINTR` when doing I/O on files or sockets (#657, Simon McVittie)
+
+  * Don't make assumptions about alignment of socket control message data
+    (#637, Simon McVittie)
+
+  * Silence some Meson deprecation warnings (#647, @Sertonix)
+
+  * Update URLs in documentation to https (#566, @TotalCaesar659)
+
+  * Improve tests' compatibility with busybox (#627, @Sertonix)
+
+  * Improve compatibility with Meson < 1.3.0 (#664, Simon McVittie)
+
+Internal changes:
+
+  * Consistently use `<stdbool.h>` for booleans (#660, Simon McVittie)
+
+  * Avoid `-Wshadow` compiler warnings (#661, Simon McVittie)
+
+  * Update Github Actions configuration (#658, Simon McVittie)
+
+----
+
+See also <https://github.com/containers/bubblewrap/releases>
diff --git a/subprojects/bubblewrap/README.md b/subprojects/bubblewrap/README.md
index 0d1b0e0a156fa35a5afb6e5eb353d584fde62c47..c16cd7d89ad2ff842ba2adff5433f0881aeccafd 100644
--- a/subprojects/bubblewrap/README.md
+++ b/subprojects/bubblewrap/README.md
@@ -77,7 +77,7 @@ Users
 This program can be shared by all container tools which perform
 non-root operation, such as:
 
- - [Flatpak](http://www.flatpak.org)
+ - [Flatpak](https://www.flatpak.org)
  - [rpm-ostree unprivileged](https://github.com/projectatomic/rpm-ostree/pull/209)
  - [bwrap-oci](https://github.com/projectatomic/bwrap-oci)
 
@@ -92,25 +92,15 @@ Installation
 bubblewrap is available in the package repositories of the most Linux distributions
 and can be installed from there.
 
-If you need to build bubblewrap from source, you can do this with meson or autotools.
+If you need to build bubblewrap from source, you can do this with meson:
 
-meson:
-
-```
+```sh
 meson _builddir
 meson compile -C _builddir
 meson test -C _builddir
 meson install -C _builddir
 ```
 
-autotools:
-
-```
-./autogen.sh
-make
-sudo make install
-```
-
 Usage
 -----
 
@@ -155,18 +145,18 @@ Any such directories you specify mounted `nodev` by default, and can be made rea
 
 Additionally you can use these kernel features:
 
-User namespaces ([CLONE_NEWUSER](http://linux.die.net/man/2/clone)): This hides all but the current uid and gid from the
+User namespaces ([CLONE_NEWUSER](https://linux.die.net/man/2/clone)): This hides all but the current uid and gid from the
 sandbox. You can also change what the value of uid/gid should be in the sandbox.
 
-IPC namespaces ([CLONE_NEWIPC](http://linux.die.net/man/2/clone)): The sandbox will get its own copy of all the
+IPC namespaces ([CLONE_NEWIPC](https://linux.die.net/man/2/clone)): The sandbox will get its own copy of all the
 different forms of IPCs, like SysV shared memory and semaphores.
 
-PID namespaces ([CLONE_NEWPID](http://linux.die.net/man/2/clone)): The sandbox will not see any processes outside the sandbox. Additionally, bubblewrap will run a trivial pid1 inside your container to handle the requirements of reaping children in the sandbox. This avoids what is known now as the [Docker pid 1 problem](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/).
+PID namespaces ([CLONE_NEWPID](https://linux.die.net/man/2/clone)): The sandbox will not see any processes outside the sandbox. Additionally, bubblewrap will run a trivial pid1 inside your container to handle the requirements of reaping children in the sandbox. This avoids what is known now as the [Docker pid 1 problem](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/).
 
 
-Network namespaces ([CLONE_NEWNET](http://linux.die.net/man/2/clone)): The sandbox will not see the network. Instead it will have its own network namespace with only a loopback device.
+Network namespaces ([CLONE_NEWNET](https://linux.die.net/man/2/clone)): The sandbox will not see the network. Instead it will have its own network namespace with only a loopback device.
 
-UTS namespace ([CLONE_NEWUTS](http://linux.die.net/man/2/clone)): The sandbox will have its own hostname.
+UTS namespace ([CLONE_NEWUTS](https://linux.die.net/man/2/clone)): The sandbox will have its own hostname.
 
 Seccomp filters: You can pass in seccomp filters that limit which syscalls can be done in the sandbox. For more information, see [Seccomp](https://en.wikipedia.org/wiki/Seccomp).
 
diff --git a/subprojects/bubblewrap/autogen.sh b/subprojects/bubblewrap/autogen.sh
deleted file mode 100755
index 2d83fafaef9267337398b05435232e457254812c..0000000000000000000000000000000000000000
--- a/subprojects/bubblewrap/autogen.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/sh
-
-test -n "$srcdir" || srcdir=$(dirname "$0")
-test -n "$srcdir" || srcdir=.
-
-olddir=$(pwd)
-cd "$srcdir"
-
-if ! (autoreconf --version >/dev/null 2>&1); then
-        echo "*** No autoreconf found, please install it ***"
-        exit 1
-fi
-
-mkdir -p m4
-
-autoreconf --force --install --verbose
-
-cd "$olddir"
-test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
diff --git a/subprojects/bubblewrap/bind-mount.c b/subprojects/bubblewrap/bind-mount.c
index 2757caea8f16fe463b30234ee09822b8fe0fed74..a2e1ac6c40741aba9d566fad54691a6552bc859e 100644
--- a/subprojects/bubblewrap/bind-mount.c
+++ b/subprojects/bubblewrap/bind-mount.c
@@ -76,7 +76,7 @@ match_token (const char *token, const char *token_end, const char *str)
   if (token == token_end)
     return *str == 0;
 
-  return FALSE;
+  return false;
 }
 
 static unsigned long
@@ -281,12 +281,12 @@ parse_mountinfo (int  proc_fd,
         die ("Can't parse mountinfo line");
       rest = line + consumed;
 
-      rest = skip_token (rest, TRUE); /* mountroot */
+      rest = skip_token (rest, true); /* mountroot */
       mountpoint = rest;
-      rest = skip_token (rest, FALSE); /* mountpoint */
+      rest = skip_token (rest, false); /* mountpoint */
       mountpoint_end = rest++;
       options = rest;
-      rest = skip_token (rest, FALSE); /* vfs options */
+      rest = skip_token (rest, false); /* vfs options */
       options_end = rest;
 
       *mountpoint_end = 0;
@@ -324,7 +324,7 @@ parse_mountinfo (int  proc_fd,
       MountInfoLine *parent = by_id[this->parent_id];
       MountInfoLine **to_sibling;
       MountInfoLine *sibling;
-      bool covered = FALSE;
+      bool covered = false;
 
       if (!has_path_prefix (this->mountpoint, root_mount))
         continue;
@@ -333,7 +333,7 @@ parse_mountinfo (int  proc_fd,
         continue;
 
       if (strcmp (parent->mountpoint, this->mountpoint) == 0)
-        parent->covered = TRUE;
+        parent->covered = true;
 
       to_sibling = &parent->first_child;
       sibling = parent->first_child;
@@ -344,7 +344,7 @@ parse_mountinfo (int  proc_fd,
            * covered by the sibling, and we drop it. */
           if (has_path_prefix (this->mountpoint, sibling->mountpoint))
             {
-              covered = TRUE;
+              covered = true;
               break;
             }
 
@@ -405,7 +405,7 @@ bind_mount (int           proc_fd,
   if (resolved_dest == NULL)
     return BIND_MOUNT_ERROR_REALPATH_DEST;
 
-  dest_fd = open (resolved_dest, O_PATH | O_CLOEXEC);
+  dest_fd = TEMP_FAILURE_RETRY (open (resolved_dest, O_PATH | O_CLOEXEC));
   if (dest_fd < 0)
     {
       if (failing_path != NULL)
@@ -499,7 +499,7 @@ bind_mount_result_to_string (bind_mount_result res,
                              bool *want_errno_p)
 {
   char *string = NULL;
-  bool want_errno = TRUE;
+  bool want_errno = true;
 
   switch (res)
     {
@@ -521,7 +521,7 @@ bind_mount_result_to_string (bind_mount_result res,
 
       case BIND_MOUNT_ERROR_FIND_DEST_MOUNT:
         string = xasprintf ("Unable to find \"%s\" in mount table", failing_path);
-        want_errno = FALSE;
+        want_errno = false;
         break;
 
       case BIND_MOUNT_ERROR_REMOUNT_DEST:
@@ -557,9 +557,12 @@ die_with_bind_result (bind_mount_result res,
                       ...)
 {
   va_list args;
-  bool want_errno = TRUE;
+  bool want_errno = true;
   char *message;
 
+  if (bwrap_level_prefix)
+    fprintf (stderr, "<%d>", LOG_ERR);
+
   fprintf (stderr, "bwrap: ");
 
   va_start (args, format);
diff --git a/subprojects/bubblewrap/bubblewrap.c b/subprojects/bubblewrap/bubblewrap.c
index bc75da470aec092ec9bdd08c240870507acedd47..f8728c7e127838201061318c32ee42fe4533eeae 100644
--- a/subprojects/bubblewrap/bubblewrap.c
+++ b/subprojects/bubblewrap/bubblewrap.c
@@ -44,15 +44,6 @@
 #define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */
 #endif
 
-#ifndef TEMP_FAILURE_RETRY
-#define TEMP_FAILURE_RETRY(expression) \
-  (__extension__                                                              \
-    ({ long int __result;                                                     \
-       do __result = (long int) (expression);                                 \
-       while (__result == -1L && errno == EINTR);                             \
-       __result; }))
-#endif
-
 /* We limit the size of a tmpfs to half the architecture's address space,
  * to avoid hitting arbitrary limits in the kernel.
  * For example, on at least one x86_64 machine, the actual limit seems to be
@@ -74,19 +65,19 @@ static bool opt_as_pid_1;
 
 static const char *opt_argv0 = NULL;
 static const char *opt_chdir_path = NULL;
-static bool opt_assert_userns_disabled = FALSE;
-static bool opt_disable_userns = FALSE;
-static bool opt_unshare_user = FALSE;
-static bool opt_unshare_user_try = FALSE;
-static bool opt_unshare_pid = FALSE;
-static bool opt_unshare_ipc = FALSE;
-static bool opt_unshare_net = FALSE;
-static bool opt_unshare_uts = FALSE;
-static bool opt_unshare_cgroup = FALSE;
-static bool opt_unshare_cgroup_try = FALSE;
-static bool opt_needs_devpts = FALSE;
-static bool opt_new_session = FALSE;
-static bool opt_die_with_parent = FALSE;
+static bool opt_assert_userns_disabled = false;
+static bool opt_disable_userns = false;
+static bool opt_unshare_user = false;
+static bool opt_unshare_user_try = false;
+static bool opt_unshare_pid = false;
+static bool opt_unshare_ipc = false;
+static bool opt_unshare_net = false;
+static bool opt_unshare_uts = false;
+static bool opt_unshare_cgroup = false;
+static bool opt_unshare_cgroup_try = false;
+static bool opt_needs_devpts = false;
+static bool opt_new_session = false;
+static bool opt_die_with_parent = false;
 static uid_t opt_sandbox_uid = -1;
 static gid_t opt_sandbox_gid = -1;
 static int opt_sync_fd = -1;
@@ -100,8 +91,10 @@ static char *opt_args_data = NULL;  /* owned */
 static int opt_userns_fd = -1;
 static int opt_userns2_fd = -1;
 static int opt_pidns_fd = -1;
+static int opt_tmp_overlay_count = 0;
 static int next_perms = -1;
 static size_t next_size_arg = 0;
+static int next_overlay_src_count = 0;
 
 #define CAP_TO_MASK_0(x) (1L << ((x) & 31))
 #define CAP_TO_MASK_1(x) CAP_TO_MASK_0(x - 32)
@@ -131,6 +124,10 @@ typedef enum {
   SETUP_BIND_MOUNT,
   SETUP_RO_BIND_MOUNT,
   SETUP_DEV_BIND_MOUNT,
+  SETUP_OVERLAY_MOUNT,
+  SETUP_TMP_OVERLAY_MOUNT,
+  SETUP_RO_OVERLAY_MOUNT,
+  SETUP_OVERLAY_SRC,
   SETUP_MOUNT_PROC,
   SETUP_MOUNT_DEV,
   SETUP_MOUNT_TMPFS,
@@ -176,6 +173,7 @@ struct _LockFile
 enum {
   PRIV_SEP_OP_DONE,
   PRIV_SEP_OP_BIND_MOUNT,
+  PRIV_SEP_OP_OVERLAY_MOUNT,
   PRIV_SEP_OP_PROC_MOUNT,
   PRIV_SEP_OP_TMPFS_MOUNT,
   PRIV_SEP_OP_DEVPTS_MOUNT,
@@ -311,6 +309,7 @@ usage (int ecode, FILE *out)
            "    --version                    Print version\n"
            "    --args FD                    Parse NUL-separated args from FD\n"
            "    --argv0 VALUE                Set argv[0] to the value VALUE before running the program\n"
+           "    --level-prefix               Prepend e.g. <3> to diagnostic messages\n"
            "    --unshare-all                Unshare every namespace we support by default\n"
            "    --share-net                  Retain the network namespace (can only combine with --unshare-all)\n"
            "    --unshare-user               Create new user namespace (may be automatically implied if not setuid)\n"
@@ -344,6 +343,11 @@ usage (int ecode, FILE *out)
            "    --bind-fd FD DEST            Bind open directory or path fd on DEST\n"
            "    --ro-bind-fd FD DEST         Bind open directory or path fd read-only on DEST\n"
            "    --remount-ro DEST            Remount DEST as readonly; does not recursively remount\n"
+           "    --overlay-src SRC            Read files from SRC in the following overlay\n"
+           "    --overlay RWSRC WORKDIR DEST Mount overlayfs on DEST, with RWSRC as the host path for writes and\n"
+           "                                 WORKDIR an empty directory on the same filesystem as RWSRC\n"
+           "    --tmp-overlay DEST           Mount overlayfs on DEST, with writes going to an invisible tmpfs\n"
+           "    --ro-overlay DEST            Mount overlayfs read-only on DEST\n"
            "    --exec-label LABEL           Exec label for the sandbox\n"
            "    --file-label LABEL           File label for temporary sandbox content\n"
            "    --proc DEST                  Mount new procfs on DEST\n"
@@ -476,7 +480,7 @@ report_child_exit_status (int exitc, int setup_finished_fd)
     return;
 
   output = xasprintf ("{ \"exit-code\": %i }\n", exitc);
-  dump_info (opt_json_status_fd, output, FALSE);
+  dump_info (opt_json_status_fd, output, false);
   close (opt_json_status_fd);
   opt_json_status_fd = -1;
   close (setup_finished_fd);
@@ -598,7 +602,7 @@ do_init (int event_fd, pid_t initial_pid)
 
   for (lock = lock_files; lock != NULL; lock = lock->next)
     {
-      int fd = open (lock->path, O_RDONLY | O_CLOEXEC);
+      int fd = TEMP_FAILURE_RETRY (open (lock->path, O_RDONLY | O_CLOEXEC));
       if (fd == -1)
         die_with_error ("Unable to open lock file %s", lock->path);
 
@@ -609,7 +613,7 @@ do_init (int event_fd, pid_t initial_pid)
         .l_len = 0
       };
 
-      if (fcntl (fd, F_SETLK, &l) < 0)
+      if (TEMP_FAILURE_RETRY (fcntl (fd, F_SETLK, &l)) < 0)
         die_with_error ("Unable to lock file %s", lock->path);
 
       /* Keep fd open to hang on to lock */
@@ -621,12 +625,12 @@ do_init (int event_fd, pid_t initial_pid)
 
   seccomp_programs_apply ();
 
-  while (TRUE)
+  while (true)
     {
       pid_t child;
       int status;
 
-      child = wait (&status);
+      child = TEMP_FAILURE_RETRY (wait (&status));
       if (child == initial_pid)
         {
           initial_exit_status = propagate_exit_status (status);
@@ -637,7 +641,7 @@ do_init (int event_fd, pid_t initial_pid)
               int res UNUSED;
 
               val = initial_exit_status + 1;
-              res = write (event_fd, &val, 8);
+              res = TEMP_FAILURE_RETRY (write (event_fd, &val, 8));
               /* Ignore res, if e.g. the parent died and closed event_fd
                  we don't want to error out here */
             }
@@ -765,16 +769,16 @@ prctl_caps (uint32_t *caps, bool do_cap_bounding, bool do_set_ambient)
    */
   for (cap = 0; cap <= CAP_LAST_CAP; cap++)
     {
-      bool keep = FALSE;
+      bool keep = false;
       if (cap < 32)
         {
           if (CAP_TO_MASK_0 (cap) & caps[0])
-            keep = TRUE;
+            keep = true;
         }
       else
         {
           if (CAP_TO_MASK_1 (cap) & caps[1])
-            keep = TRUE;
+            keep = true;
         }
 
       if (keep && do_set_ambient)
@@ -803,11 +807,11 @@ static void
 drop_cap_bounding_set (bool drop_all)
 {
   if (!drop_all)
-    prctl_caps (requested_caps, TRUE, FALSE);
+    prctl_caps (requested_caps, true, false);
   else
     {
       uint32_t no_caps[2] = {0, 0};
-      prctl_caps (no_caps, TRUE, FALSE);
+      prctl_caps (no_caps, true, false);
     }
 }
 
@@ -816,13 +820,13 @@ set_ambient_capabilities (void)
 {
   if (is_privileged)
     return;
-  prctl_caps (requested_caps, FALSE, TRUE);
+  prctl_caps (requested_caps, false, true);
 }
 
 /* This acquires the privileges that the bwrap will need it to work.
  * If bwrap is not setuid, then this does nothing, and it relies on
  * unprivileged user namespaces to be used. This case is
- * "is_privileged = FALSE".
+ * "is_privileged = false".
  *
  * If bwrap is setuid, then we do things in phases.
  * The first part is run as euid 0, but with fsuid as the real user.
@@ -846,7 +850,7 @@ acquire_privs (void)
       if (euid != 0)
         die ("Unexpected setuid user %d, should be 0", euid);
 
-      is_privileged = TRUE;
+      is_privileged = true;
       /* We want to keep running as euid=0 until at the clone()
        * operation because doing so will make the user namespace be
        * owned by root, which makes it not ptrace:able by the user as
@@ -867,7 +871,7 @@ acquire_privs (void)
         die ("Unable to set fsuid (was %d)", (int)new_fsuid);
 
       /* We never need capabilities after execve(), so lets drop everything from the bounding set */
-      drop_cap_bounding_set (TRUE);
+      drop_cap_bounding_set (true);
 
       /* Keep only the required capabilities for setup */
       set_required_caps ();
@@ -904,7 +908,7 @@ switch_to_user_with_privs (void)
 {
   /* If we're in a new user namespace, we got back the bounding set, clear it again */
   if (opt_unshare_user || opt_userns_fd != -1)
-    drop_cap_bounding_set (FALSE);
+    drop_cap_bounding_set (false);
 
   /* If we switched to a new user namespace it may allow other uids/gids, so switch to the target one */
   if (opt_userns_fd != -1)
@@ -1061,10 +1065,10 @@ privileged_op (int         privileged_op_socket,
       if (arg2 != NULL)
         strcpy ((char *) buffer + arg2_offset, arg2);
 
-      if (write (privileged_op_socket, buffer, buffer_size) != (ssize_t)buffer_size)
+      if (TEMP_FAILURE_RETRY (write (privileged_op_socket, buffer, buffer_size)) != (ssize_t)buffer_size)
         die ("Can't write to privileged_op_socket");
 
-      if (read (privileged_op_socket, buffer, 1) != 1)
+      if (TEMP_FAILURE_RETRY (read (privileged_op_socket, buffer, 1)) != 1)
         die ("Can't read from privileged_op_socket");
 
       return;
@@ -1149,6 +1153,20 @@ privileged_op (int         privileged_op_socket,
         die_with_mount_error ("Can't mount mqueue on %s", arg1);
       break;
 
+    case PRIV_SEP_OP_OVERLAY_MOUNT:
+      if (mount ("overlay", arg2, "overlay", MS_MGC_VAL, arg1) != 0)
+        {
+          /* The standard message for ELOOP, "Too many levels of symbolic
+           * links", is not helpful here. */
+          if (errno == ELOOP)
+            die ("Can't make overlay mount on %s with options %s: "
+                "Overlay directories may not overlap",
+                arg2, arg1);
+          die_with_mount_error ("Can't make overlay mount on %s with options %s",
+                                arg2, arg1);
+        }
+      break;
+
     case PRIV_SEP_OP_SET_HOSTNAME:
       /* This is checked at the start, but lets verify it here in case
          something manages to send hacked priv-sep operation requests. */
@@ -1172,6 +1190,7 @@ setup_newroot (bool unshare_pid,
                int  privileged_op_socket)
 {
   SetupOp *op;
+  int tmp_overlay_idx = 0;
 
   for (op = ops; op != NULL; op = op->next)
     {
@@ -1211,7 +1230,7 @@ setup_newroot (bool unshare_pid,
             parent_mode &= ~0005U;
 
           dest = get_newroot_path (op->dest);
-          if (mkdir_with_parents (dest, parent_mode, FALSE) != 0)
+          if (mkdir_with_parents (dest, parent_mode, false) != 0)
             die_with_error ("Can't mkdir parents for %s", op->dest);
         }
 
@@ -1259,6 +1278,47 @@ setup_newroot (bool unshare_pid,
 
           break;
 
+        case SETUP_OVERLAY_MOUNT:
+        case SETUP_RO_OVERLAY_MOUNT:
+        case SETUP_TMP_OVERLAY_MOUNT:
+          {
+            StringBuilder sb = {0};
+            bool multi_src = false;
+
+            if (ensure_dir (dest, 0755) != 0)
+              die_with_error ("Can't mkdir %s", op->dest);
+
+            if (op->source != NULL)
+              {
+                strappend (&sb, "upperdir=/oldroot");
+                strappend_escape_for_mount_options (&sb, op->source);
+                strappend (&sb, ",workdir=/oldroot");
+                op = op->next;
+                strappend_escape_for_mount_options (&sb, op->source);
+                strappend (&sb, ",");
+              }
+            else if (op->type == SETUP_TMP_OVERLAY_MOUNT)
+              strappendf (&sb, "upperdir=/tmp-overlay-upper-%1$d,workdir=/tmp-overlay-work-%1$d,",
+                          tmp_overlay_idx++);
+
+            strappend (&sb, "lowerdir=/oldroot");
+            while (op->next != NULL && op->next->type == SETUP_OVERLAY_SRC)
+              {
+                op = op->next;
+                if (multi_src)
+                  strappend (&sb, ":/oldroot");
+                strappend_escape_for_mount_options (&sb, op->source);
+                multi_src = true;
+              }
+
+            strappend (&sb, ",userxattr");
+
+            privileged_op (privileged_op_socket,
+                           PRIV_SEP_OP_OVERLAY_MOUNT, 0, 0, 0, sb.str, dest);
+            free (sb.str);
+          }
+          break;
+
         case SETUP_REMOUNT_RO_NO_RECURSIVE:
           privileged_op (privileged_op_socket,
                          PRIV_SEP_OP_REMOUNT_RO_NO_RECURSIVE, 0, 0, 0, NULL, dest);
@@ -1522,6 +1582,7 @@ setup_newroot (bool unshare_pid,
                          op->dest, NULL);
           break;
 
+        case SETUP_OVERLAY_SRC:  /* handled by SETUP_OVERLAY_MOUNT */
         default:
           die ("Unexpected type %d", op->type);
         }
@@ -1564,6 +1625,8 @@ resolve_symlinks_in_ops (void)
         case SETUP_RO_BIND_MOUNT:
         case SETUP_DEV_BIND_MOUNT:
         case SETUP_BIND_MOUNT:
+        case SETUP_OVERLAY_SRC:
+        case SETUP_OVERLAY_MOUNT:
           old_source = op->source;
           op->source = realpath (old_source, NULL);
           if (op->source == NULL)
@@ -1575,6 +1638,8 @@ resolve_symlinks_in_ops (void)
             }
           break;
 
+        case SETUP_RO_OVERLAY_MOUNT:
+        case SETUP_TMP_OVERLAY_MOUNT:
         case SETUP_MOUNT_PROC:
         case SETUP_MOUNT_DEV:
         case SETUP_MOUNT_TMPFS:
@@ -1666,6 +1731,32 @@ warn_only_last_option (const char *name)
   warn ("Only the last %s option will take effect", name);
 }
 
+static void
+make_setup_overlay_src_ops (const char *const *const argv)
+{
+  /* SETUP_OVERLAY_SRC is unlike other SETUP_* ops in that it exists to hold
+   * data for SETUP_{,TMP_,RO_}OVERLAY_MOUNT ops, not to be its own operation.
+   * This lets us reuse existing code paths to handle resolving the realpaths
+   * of each source, as no other operations involve multiple sources the way
+   * the *_OVERLAY_MOUNT ops do.
+   *
+   * While the --overlay-src arguments are expected to (directly) precede the
+   * --overlay argument, in bottom-to-top order, the SETUP_OVERLAY_SRC ops
+   * follow their corresponding *_OVERLAY_MOUNT op, in top-to-bottom order
+   * (the order in which overlayfs will want them). They are handled specially
+   * in setup_new_root () during the processing of *_OVERLAY_MOUNT.
+   */
+  int i;
+  SetupOp *op;
+
+  for (i = 1; i <= next_overlay_src_count; i++)
+    {
+      op = setup_op_new (SETUP_OVERLAY_SRC);
+      op->source = argv[1 - 2 * i];
+    }
+  next_overlay_src_count = 0;
+}
+
 static void
 parse_args_recurse (int          *argcp,
                     const char ***argvp,
@@ -1761,7 +1852,7 @@ parse_args_recurse (int          *argcp,
             }
 
           data_argv_copy = data_argv; /* Don't change data_argv, we need to free it */
-          parse_args_recurse (&data_argc, &data_argv_copy, TRUE, total_parsed_argc_p);
+          parse_args_recurse (&data_argc, &data_argv_copy, true, total_parsed_argc_p);
 
           argv += 1;
           argc -= 1;
@@ -1778,6 +1869,10 @@ parse_args_recurse (int          *argcp,
           argv++;
           argc--;
         }
+      else if (strcmp (arg, "--level-prefix") == 0)
+        {
+          bwrap_level_prefix = true;
+        }
       else if (strcmp (arg, "--unshare-all") == 0)
         {
           /* Keep this in order with the older (legacy) --unshare arguments,
@@ -1786,45 +1881,45 @@ parse_args_recurse (int          *argcp,
            */
           opt_unshare_user_try = opt_unshare_ipc = opt_unshare_pid =
             opt_unshare_uts = opt_unshare_cgroup_try =
-            opt_unshare_net = TRUE;
+            opt_unshare_net = true;
         }
       /* Begin here the older individual --unshare variants */
       else if (strcmp (arg, "--unshare-user") == 0)
         {
-          opt_unshare_user = TRUE;
+          opt_unshare_user = true;
         }
       else if (strcmp (arg, "--unshare-user-try") == 0)
         {
-          opt_unshare_user_try = TRUE;
+          opt_unshare_user_try = true;
         }
       else if (strcmp (arg, "--unshare-ipc") == 0)
         {
-          opt_unshare_ipc = TRUE;
+          opt_unshare_ipc = true;
         }
       else if (strcmp (arg, "--unshare-pid") == 0)
         {
-          opt_unshare_pid = TRUE;
+          opt_unshare_pid = true;
         }
       else if (strcmp (arg, "--unshare-net") == 0)
         {
-          opt_unshare_net = TRUE;
+          opt_unshare_net = true;
         }
       else if (strcmp (arg, "--unshare-uts") == 0)
         {
-          opt_unshare_uts = TRUE;
+          opt_unshare_uts = true;
         }
       else if (strcmp (arg, "--unshare-cgroup") == 0)
         {
-          opt_unshare_cgroup = TRUE;
+          opt_unshare_cgroup = true;
         }
       else if (strcmp (arg, "--unshare-cgroup-try") == 0)
         {
-          opt_unshare_cgroup_try = TRUE;
+          opt_unshare_cgroup_try = true;
         }
       /* Begin here the newer --share variants */
       else if (strcmp (arg, "--share-net") == 0)
         {
-          opt_unshare_net = FALSE;
+          opt_unshare_net = false;
         }
       /* End --share variants, other arguments begin */
       else if (strcmp (arg, "--chdir") == 0)
@@ -1841,11 +1936,11 @@ parse_args_recurse (int          *argcp,
         }
       else if (strcmp (arg, "--disable-userns") == 0)
         {
-          opt_disable_userns = TRUE;
+          opt_disable_userns = true;
         }
       else if (strcmp (arg, "--assert-userns-disabled") == 0)
         {
-          opt_assert_userns_disabled = TRUE;
+          opt_assert_userns_disabled = true;
         }
       else if (strcmp (arg, "--remount-ro") == 0)
         {
@@ -1927,6 +2022,76 @@ parse_args_recurse (int          *argcp,
           argv += 2;
           argc -= 2;
         }
+      else if (strcmp (arg, "--overlay-src") == 0)
+        {
+          if (is_privileged)
+            die ("The --overlay-src option is not permitted in setuid mode");
+
+          next_overlay_src_count++;
+
+          argv += 1;
+          argc -= 1;
+        }
+      else if (strcmp (arg, "--overlay") == 0)
+        {
+          SetupOp *workdir_op;
+
+          if (is_privileged)
+            die ("The --overlay option is not permitted in setuid mode");
+
+          if (argc < 4)
+            die ("--overlay takes three arguments");
+
+          if (next_overlay_src_count < 1)
+            die ("--overlay requires at least one --overlay-src");
+
+          op = setup_op_new (SETUP_OVERLAY_MOUNT);
+          op->source = argv[1];
+          workdir_op = setup_op_new (SETUP_OVERLAY_SRC);
+          workdir_op->source = argv[2];
+          op->dest = argv[3];
+          make_setup_overlay_src_ops (argv);
+
+          argv += 3;
+          argc -= 3;
+        }
+      else if (strcmp (arg, "--tmp-overlay") == 0)
+        {
+          if (is_privileged)
+            die ("The --tmp-overlay option is not permitted in setuid mode");
+
+          if (argc < 2)
+            die ("--tmp-overlay takes an argument");
+
+          if (next_overlay_src_count < 1)
+            die ("--tmp-overlay requires at least one --overlay-src");
+
+          op = setup_op_new (SETUP_TMP_OVERLAY_MOUNT);
+          op->dest = argv[1];
+          make_setup_overlay_src_ops (argv);
+          opt_tmp_overlay_count++;
+
+          argv += 1;
+          argc -= 1;
+        }
+      else if (strcmp (arg, "--ro-overlay") == 0)
+        {
+          if (is_privileged)
+            die ("The --ro-overlay option is not permitted in setuid mode");
+
+          if (argc < 2)
+            die ("--ro-overlay takes an argument");
+
+          if (next_overlay_src_count < 2)
+            die ("--ro-overlay requires at least two --overlay-src");
+
+          op = setup_op_new (SETUP_RO_OVERLAY_MOUNT);
+          op->dest = argv[1];
+          make_setup_overlay_src_ops (argv);
+
+          argv += 1;
+          argc -= 1;
+        }
       else if (strcmp (arg, "--proc") == 0)
         {
           if (argc < 2)
@@ -1975,7 +2140,7 @@ parse_args_recurse (int          *argcp,
 
           op = setup_op_new (SETUP_MOUNT_DEV);
           op->dest = argv[1];
-          opt_needs_devpts = TRUE;
+          opt_needs_devpts = true;
 
           argv += 1;
           argc -= 1;
@@ -2425,15 +2590,15 @@ parse_args_recurse (int          *argcp,
         }
       else if (strcmp (arg, "--new-session") == 0)
         {
-          opt_new_session = TRUE;
+          opt_new_session = true;
         }
       else if (strcmp (arg, "--die-with-parent") == 0)
         {
-          opt_die_with_parent = TRUE;
+          opt_die_with_parent = true;
         }
       else if (strcmp (arg, "--as-pid-1") == 0)
         {
-          opt_as_pid_1 = TRUE;
+          opt_as_pid_1 = true;
         }
       else if (strcmp (arg, "--cap-add") == 0)
         {
@@ -2441,7 +2606,7 @@ parse_args_recurse (int          *argcp,
           if (argc < 2)
             die ("--cap-add takes an argument");
 
-          opt_cap_add_or_drop_used = TRUE;
+          opt_cap_add_or_drop_used = true;
 
           if (strcasecmp (argv[1], "ALL") == 0)
             {
@@ -2467,7 +2632,7 @@ parse_args_recurse (int          *argcp,
           if (argc < 2)
             die ("--cap-drop takes an argument");
 
-          opt_cap_add_or_drop_used = TRUE;
+          opt_cap_add_or_drop_used = true;
 
           if (strcasecmp (argv[1], "ALL") == 0)
             {
@@ -2596,6 +2761,10 @@ parse_args_recurse (int          *argcp,
       if (!is_modifier_option(arg) && next_size_arg != 0)
         die ("--size must be followed by --tmpfs");
 
+      /* Similarly for --overlay-src. */
+      if (strcmp (arg, "--overlay-src") != 0 && next_overlay_src_count > 0)
+        die ("--overlay-src must be followed by another --overlay-src or one of --overlay, --tmp-overlay, or --ro-overlay");
+
       argv++;
       argc--;
     }
@@ -2610,7 +2779,10 @@ parse_args (int          *argcp,
 {
   int total_parsed_argc = *argcp;
 
-  parse_args_recurse (argcp, argvp, FALSE, &total_parsed_argc);
+  parse_args_recurse (argcp, argvp, false, &total_parsed_argc);
+
+  if (next_overlay_src_count > 0)
+    die ("--overlay-src must be followed by another --overlay-src or one of --overlay, --tmp-overlay, or --ro-overlay");
 }
 
 static void
@@ -2644,7 +2816,7 @@ namespace_ids_read (pid_t  pid)
   NsInfo *info;
 
   dir = xasprintf ("%d/ns", pid);
-  ns_fd = openat (proc_fd, dir, O_PATH);
+  ns_fd = TEMP_FAILURE_RETRY (openat (proc_fd, dir, O_PATH));
 
   if (ns_fd < 0)
     die_with_error ("open /proc/%s/ns failed", dir);
@@ -2656,7 +2828,7 @@ namespace_ids_read (pid_t  pid)
       int r;
 
       /* if we don't unshare this ns, ignore it */
-      if (do_unshare && *do_unshare == FALSE)
+      if (do_unshare && *do_unshare == false)
         continue;
 
       r = fstatat (ns_fd, info->name, &st, 0);
@@ -2691,7 +2863,7 @@ namespace_ids_write (int    fd,
       output = xasprintf (",%s\"%s-namespace\": %ju",
                           indent, info->name, nsid);
 
-      dump_info (fd, output, TRUE);
+      dump_info (fd, output, true);
     }
 }
 
@@ -2716,6 +2888,7 @@ main (int    argc,
   cleanup_free char *args_data UNUSED = NULL;
   int intermediate_pids_sockets[2] = {-1, -1};
   const char *exec_path = NULL;
+  int i;
 
   /* Handle --version early on before we try to acquire/drop
    * any capabilities so it works in a build environment;
@@ -2799,18 +2972,18 @@ main (int    argc,
   /* We have to do this if we weren't installed setuid (and we're not
    * root), so let's just DWIM */
   if (!is_privileged && getuid () != 0 && opt_userns_fd == -1)
-    opt_unshare_user = TRUE;
+    opt_unshare_user = true;
 
 #ifdef ENABLE_REQUIRE_USERNS
   /* In this build option, we require userns. */
   if (is_privileged && getuid () != 0 && opt_userns_fd == -1)
-    opt_unshare_user = TRUE;
+    opt_unshare_user = true;
 #endif
 
   if (opt_unshare_user_try &&
       stat ("/proc/self/ns/user", &sbuf) == 0)
     {
-      bool disabled = FALSE;
+      bool disabled = false;
 
       /* RHEL7 has a kernel module parameter that lets you enable user namespaces */
       if (stat ("/sys/module/user_namespace/parameters/enable", &sbuf) == 0)
@@ -2818,7 +2991,7 @@ main (int    argc,
           cleanup_free char *enable = NULL;
           enable = load_file_at (AT_FDCWD, "/sys/module/user_namespace/parameters/enable");
           if (enable != NULL && enable[0] == 'N')
-            disabled = TRUE;
+            disabled = true;
         }
 
       /* Check for max_user_namespaces */
@@ -2827,21 +3000,21 @@ main (int    argc,
           cleanup_free char *max_user_ns = NULL;
           max_user_ns = load_file_at (AT_FDCWD, "/proc/sys/user/max_user_namespaces");
           if (max_user_ns != NULL && strcmp(max_user_ns, "0\n") == 0)
-            disabled = TRUE;
+            disabled = true;
         }
 
       /* Debian lets you disable *unprivileged* user namespaces. However this is not
-         a problem if we're privileged, and if we're not opt_unshare_user is TRUE
+         a problem if we're privileged, and if we're not opt_unshare_user is true
          already, and there is not much we can do, its just a non-working setup. */
 
       if (!disabled)
-        opt_unshare_user = TRUE;
+        opt_unshare_user = true;
     }
 
   if (argc <= 0)
     usage (EXIT_FAILURE, stderr);
 
-  __debug__ (("Creating root mount point\n"));
+  debug ("Creating root mount point");
 
   if (opt_sandbox_uid == (uid_t)-1)
     opt_sandbox_uid = real_uid;
@@ -2865,7 +3038,7 @@ main (int    argc,
 
   /* We need to read stuff from proc during the pivot_root dance, etc.
      Lets keep a fd to it open */
-  proc_fd = open ("/proc", O_PATH);
+  proc_fd = TEMP_FAILURE_RETRY (open ("/proc", O_PATH));
   if (proc_fd == -1)
     die_with_error ("Can't open /proc");
 
@@ -2877,7 +3050,7 @@ main (int    argc,
    * access ourselves. */
   base_path = "/tmp";
 
-  __debug__ (("creating new namespace\n"));
+  debug ("creating new namespace");
 
   if (opt_unshare_pid && !opt_as_pid_1)
     {
@@ -2993,7 +3166,7 @@ main (int    argc,
            */
           write_uid_gid_map (ns_uid, real_uid,
                              ns_gid, real_gid,
-                             pid, TRUE, opt_needs_devpts);
+                             pid, true, opt_needs_devpts);
         }
 
       /* Initial launched process, wait for pid 1 or exec:ed command to exit */
@@ -3002,7 +3175,7 @@ main (int    argc,
         die_with_error ("Setting userns2 failed");
 
       /* We don't need any privileges in the launcher, drop them immediately. */
-      drop_privs (FALSE, FALSE);
+      drop_privs (false, false);
 
       /* Optionally bind our lifecycle to that of the parent */
       handle_die_with_parent ();
@@ -3010,17 +3183,17 @@ main (int    argc,
       if (opt_info_fd != -1)
         {
           cleanup_free char *output = xasprintf ("{\n    \"child-pid\": %i", pid);
-          dump_info (opt_info_fd, output, TRUE);
-          namespace_ids_write (opt_info_fd, FALSE);
-          dump_info (opt_info_fd, "\n}\n", TRUE);
+          dump_info (opt_info_fd, output, true);
+          namespace_ids_write (opt_info_fd, false);
+          dump_info (opt_info_fd, "\n}\n", true);
           close (opt_info_fd);
         }
       if (opt_json_status_fd != -1)
         {
           cleanup_free char *output = xasprintf ("{ \"child-pid\": %i", pid);
-          dump_info (opt_json_status_fd, output, TRUE);
-          namespace_ids_write (opt_json_status_fd, TRUE);
-          dump_info (opt_json_status_fd, " }\n", TRUE);
+          dump_info (opt_json_status_fd, output, true);
+          namespace_ids_write (opt_json_status_fd, true);
+          dump_info (opt_json_status_fd, " }\n", true);
         }
 
       if (opt_userns_block_fd != -1)
@@ -3032,7 +3205,7 @@ main (int    argc,
 
       /* Let child run now that the uid maps are set up */
       val = 1;
-      res = write (child_wait_fd, &val, 8);
+      res = TEMP_FAILURE_RETRY (write (child_wait_fd, &val, 8));
       /* Ignore res, if e.g. the child died and closed child_wait_fd we don't want to error out here */
       close (child_wait_fd);
 
@@ -3116,7 +3289,7 @@ main (int    argc,
 
       write_uid_gid_map (ns_uid, real_uid,
                          ns_gid, real_gid,
-                         -1, TRUE, FALSE);
+                         -1, true, false);
     }
 
   old_umask = umask (0);
@@ -3156,6 +3329,19 @@ main (int    argc,
   if (mkdir ("oldroot", 0755))
     die_with_error ("Creating oldroot failed");
 
+  for (i = 0; i < opt_tmp_overlay_count; i++)
+    {
+      char *dirname;
+      dirname = xasprintf ("tmp-overlay-upper-%d", i);
+      if (mkdir (dirname, 0755))
+        die_with_error ("Creating --tmp-overlay upperdir failed");
+      free (dirname);
+      dirname = xasprintf ("tmp-overlay-work-%d", i);
+      if (mkdir (dirname, 0755))
+        die_with_error ("Creating --tmp-overlay workdir failed");
+      free (dirname);
+    }
+
   if (pivot_root (base_path, "oldroot"))
     die_with_error ("pivot_root");
 
@@ -3177,7 +3363,7 @@ main (int    argc,
       if (child == 0)
         {
           /* Unprivileged setup process */
-          drop_privs (FALSE, TRUE);
+          drop_privs (false, true);
           close (privsep_sockets[0]);
           setup_newroot (opt_unshare_pid, privsep_sockets[1]);
           exit (0);
@@ -3199,12 +3385,12 @@ main (int    argc,
               op = read_priv_sec_op (unpriv_socket, buffer, sizeof (buffer),
                                      &flags, &perms, &size_arg, &arg1, &arg2);
               privileged_op (-1, op, flags, perms, size_arg, arg1, arg2);
-              if (write (unpriv_socket, buffer, 1) != 1)
+              if (TEMP_FAILURE_RETRY (write (unpriv_socket, buffer, 1)) != 1)
                 die ("Can't write to op_socket");
             }
           while (op != PRIV_SEP_OP_DONE);
 
-          waitpid (child, &status, 0);
+          TEMP_FAILURE_RETRY (waitpid (child, &status, 0));
           /* Continue post setup */
         }
     }
@@ -3228,7 +3414,7 @@ main (int    argc,
    * We're aiming to make /newroot the real root, and get rid of /oldroot. To do
    * that we need a temporary place to store it before we can unmount it.
    */
-  { cleanup_fd int oldrootfd = open ("/", O_DIRECTORY | O_RDONLY);
+  { cleanup_fd int oldrootfd = TEMP_FAILURE_RETRY (open ("/", O_DIRECTORY | O_RDONLY));
     if (oldrootfd < 0)
       die_with_error ("can't open /");
     if (chdir ("/newroot") != 0)
@@ -3276,7 +3462,7 @@ main (int    argc,
         {
           cleanup_fd int sysctl_fd = -1;
 
-          sysctl_fd = openat (proc_fd, "sys/user/max_user_namespaces", O_WRONLY);
+          sysctl_fd = TEMP_FAILURE_RETRY (openat (proc_fd, "sys/user/max_user_namespaces", O_WRONLY));
 
           if (sysctl_fd < 0)
             die_with_error ("cannot open /proc/sys/user/max_user_namespaces");
@@ -3289,11 +3475,11 @@ main (int    argc,
         die_with_error ("unshare user ns");
 
       /* We're in a new user namespace, we got back the bounding set, clear it again */
-      drop_cap_bounding_set (FALSE);
+      drop_cap_bounding_set (false);
 
       write_uid_gid_map (opt_sandbox_uid, ns_uid,
                          opt_sandbox_gid, ns_gid,
-                         -1, FALSE, FALSE);
+                         -1, false, false);
     }
 
   if (opt_disable_userns || opt_assert_userns_disabled)
@@ -3306,7 +3492,7 @@ main (int    argc,
     }
 
   /* All privileged ops are done now, so drop caps we don't need */
-  drop_privs (!is_privileged, TRUE);
+  drop_privs (!is_privileged, true);
 
   if (opt_block_fd != -1)
     {
@@ -3354,7 +3540,7 @@ main (int    argc,
   if (label_exec (opt_exec_label) == -1)
     die_with_error ("label_exec %s", argv[0]);
 
-  __debug__ (("forking for child\n"));
+  debug ("forking for child");
 
   if (!opt_as_pid_1 && (opt_unshare_pid || lock_files != NULL || opt_sync_fd != -1))
     {
@@ -3370,7 +3556,7 @@ main (int    argc,
 
       if (pid != 0)
         {
-          drop_all_caps (FALSE);
+          drop_all_caps (false);
 
           /* Close fds in pid 1, except stdio and optionally event_fd
              (for syncing pid 2 lifetime with monitor_child) and
@@ -3392,7 +3578,7 @@ main (int    argc,
         }
     }
 
-  __debug__ (("launch executable %s\n", argv[0]));
+  debug ("launch executable %s", argv[0]);
 
   if (proc_fd != -1)
     close (proc_fd);
diff --git a/subprojects/bubblewrap/bwrap.xml b/subprojects/bubblewrap/bwrap.xml
index 3bb50820de2668f190098d67128296b42a4ad8d2..f379f0fa46a98ddb648efef6ca9e49e29b7e8c4f 100644
--- a/subprojects/bubblewrap/bwrap.xml
+++ b/subprojects/bubblewrap/bwrap.xml
@@ -96,6 +96,26 @@
       <term><option>--argv0 <arg choice="plain">VALUE</arg></option></term>
       <listitem><para>Set argv[0] to the value <arg choice="plain">VALUE</arg> before running the program</para></listitem>
     </varlistentry>
+    <varlistentry>
+      <term><option>--level-prefix</option></term>
+      <listitem>
+        <para>
+          Prefix each line of diagnostic output with a numeric severity
+          level enclosed in angle brackets.
+          The severity levels used are based on the constants used by
+          <citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>:
+          for example, <literal>&lt;4&gt;</literal> indicates a warning,
+          because <literal>LOG_WARNING</literal> has numeric value 4.
+          Numbers smaller than 4 indicate fatal errors, and numbers larger
+          than 4 indicate informational messages.
+          These prefixes can be parsed by tools compatible with
+          <literal>logger --prio-prefix</literal> (see
+          <citerefentry><refentrytitle>logger</refentrytitle><manvolnum>1</manvolnum></citerefentry>)
+          or <literal>systemd-cat --level-prefix=1</literal> (see
+          <citerefentry><refentrytitle>systemd-cat</refentrytitle><manvolnum>1</manvolnum></citerefentry>).
+        </para>
+      </listitem>
+    </varlistentry>
   </variablelist>
   <para>Options related to kernel namespaces:</para>
   <variablelist>
@@ -297,6 +317,86 @@
       <term><option>--remount-ro <arg choice="plain">DEST</arg></option></term>
       <listitem><para>Remount the path <arg choice="plain">DEST</arg> as readonly.  It works only on the specified mount point, without changing any other mount point under the specified path</para></listitem>
     </varlistentry>
+    <varlistentry>
+      <term><option>--overlay-src <arg choice="plain">SRC</arg></option></term>
+      <listitem>
+        <para>
+          This option does nothing on its own, and must be followed by one of
+          the other <literal>overlay</literal> options. It specifies a host
+          path from which files should be read if they aren't present in a
+          higher layer.
+        </para>
+        <para>
+          This option can be used multiple times to provide multiple sources.
+          The sources are overlaid in the order given, with the first source on
+          the command line at the bottom of the stack: if a given path to be
+          read exists in more than one source, the file is read from the last
+          such source specified.
+        </para>
+        <para>
+          (For readers familiar with overlayfs, note that this is the
+          reverse of the order used by the kernel's <literal>lowerdir</literal>
+          mount option.)
+        </para>
+      </listitem>
+    </varlistentry>
+    <varlistentry>
+      <term><option>--overlay <arg choice="plain">RWSRC</arg> <arg choice="plain">WORKDIR</arg> <arg choice="plain">DEST</arg></option></term>
+    </varlistentry>
+    <varlistentry>
+      <term><option>--tmp-overlay <arg choice="plain">DEST</arg></option></term>
+    </varlistentry>
+    <varlistentry>
+      <term><option>--ro-overlay <arg choice="plain">DEST</arg></option></term>
+      <listitem>
+        <para>
+          Use overlayfs to mount the host paths specified by
+          <arg choice="plain">RWSRC</arg> and all immediately preceding
+          <option>--overlay-src</option> on <arg choice="plain">DEST</arg>.
+          <arg choice="plain">DEST</arg> will contain the union of all the files
+          in all the layers.
+        </para>
+        <para>
+          With <arg choice="plain">--overlay</arg> all writes will go to
+          <arg choice="plain">RWSRC</arg>. Reads will come preferentially from
+          <arg choice="plain">RWSRC</arg>, and then from any
+          <option>--overlay-src</option> paths.
+          <arg choice="plain">WORKDIR</arg> must be an empty directory on the
+          same filesystem as <arg choice="plain">RWSRC</arg>, and is used
+          internally by the kernel.
+        </para>
+        <para>
+          With <arg choice="plain">--tmp-overlay</arg> all writes will go to
+          the tmpfs that hosts the sandbox root, in a location not accessible
+          from either the host or the child process. Writes will therefore not
+          be persisted across multiple runs.
+        </para>
+        <para>
+          With <arg choice="plain">--ro-overlay</arg> the filesystem will be
+          mounted read-only. This option requires at least two
+          <option>--overlay-src</option> to precede it.
+        </para>
+        <para>
+          None of these options are available in the setuid version of
+          bubblewrap. Using <arg choice="plain">--ro-overlay</arg> or providing
+          more than one <option>--overlay-src</option> requires a Linux kernel
+          version of 4.0 or later.
+        </para>
+        <para>
+          Due to limitations of overlayfs, no host directory given via
+          <arg choice="plain">--overlay-src</arg> or
+          <arg choice="plain">--overlay</arg> may be an ancestor of another,
+          after resolving symlinks. Depending on version, the Linux kernel may
+          or may not enforce this, but if not then overlayfs's behavior is
+          undefined.
+        </para>
+        <para>
+          For more information see the Overlay Filesystem documentation in the
+          Linux kernel at
+          https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt
+        </para>
+      </listitem>
+    </varlistentry>
     <varlistentry>
       <term><option>--proc <arg choice="plain">DEST</arg></option></term>
       <listitem><para>Mount procfs on <arg choice="plain">DEST</arg></para></listitem>
diff --git a/subprojects/bubblewrap/ci/builddeps.sh b/subprojects/bubblewrap/ci/builddeps.sh
index 4accd2e315506f8ead4db1994095640ec3b77350..5bca51e255dc2148d660df24d72f98302b8841e1 100755
--- a/subprojects/bubblewrap/ci/builddeps.sh
+++ b/subprojects/bubblewrap/ci/builddeps.sh
@@ -56,8 +56,6 @@ done
 if dpkg-vendor --derives-from Debian; then
     apt-get -y update
     apt-get -q -y install \
-        autoconf \
-        automake \
         build-essential \
         docbook-xml \
         docbook-xsl \
@@ -81,8 +79,6 @@ if command -v yum; then
     yum -y install \
         'pkgconfig(libselinux)' \
         /usr/bin/eu-readelf \
-        autoconf \
-        automake \
         docbook-style-xsl \
         gcc \
         git \
diff --git a/subprojects/bubblewrap/completions/bash/bwrap b/subprojects/bubblewrap/completions/bash/bwrap
index e6d28462b58df64833896d59cb13abfd4fee60ae..e7a523c2850036b4e882e795e7b7aab5603087e8 100644
--- a/subprojects/bubblewrap/completions/bash/bwrap
+++ b/subprojects/bubblewrap/completions/bash/bwrap
@@ -51,15 +51,19 @@ _bwrap() {
 		--hostname
 		--info-fd
 		--lock-file
+		--overlay
+		--overlay-src
 		--perms
 		--proc
 		--remount-ro
 		--ro-bind
+		--ro-overlay
 		--seccomp
 		--setenv
 		--size
 		--symlink
 		--sync-fd
+		--tmp-overlay
 		--uid
 		--unsetenv
 		--userns-block-fd
diff --git a/subprojects/bubblewrap/completions/bash/meson.build b/subprojects/bubblewrap/completions/bash/meson.build
index 1dd946f2ac4c905f1694903ad0d99589bd015d40..323ad3973bcacc41bfae7f0a864f6fc9005c7ec3 100644
--- a/subprojects/bubblewrap/completions/bash/meson.build
+++ b/subprojects/bubblewrap/completions/bash/meson.build
@@ -13,7 +13,6 @@ if bash_completion_dir == ''
         default_value: '',
         pkgconfig: 'completionsdir',
         pkgconfig_define: [
-          'prefix', get_option('prefix'),
           'datadir', get_option('prefix') / get_option('datadir'),
         ],
       )
diff --git a/subprojects/bubblewrap/configure.ac b/subprojects/bubblewrap/configure.ac
deleted file mode 100644
index 7fe9c0448119baf1b02aed9463a4524ee9ef0626..0000000000000000000000000000000000000000
--- a/subprojects/bubblewrap/configure.ac
+++ /dev/null
@@ -1,158 +0,0 @@
-AC_PREREQ([2.63])
-AC_INIT([bubblewrap], [0.10.0], [atomic-devel@projectatomic.io])
-AC_CONFIG_HEADER([config.h])
-AC_CONFIG_MACRO_DIR([m4])
-AC_CONFIG_AUX_DIR([build-aux])
-
-AC_USE_SYSTEM_EXTENSIONS
-
-AM_INIT_AUTOMAKE([1.11 -Wno-portability foreign no-define tar-ustar no-dist-gzip dist-xz])
-AM_MAINTAINER_MODE([enable])
-AM_SILENT_RULES([yes])
-
-AC_SYS_LARGEFILE
-
-AC_PROG_CC
-AM_PROG_CC_C_O
-PKG_PROG_PKG_CONFIG([])
-
-AC_CHECK_HEADERS([sys/capability.h], [], [AC_MSG_ERROR([*** POSIX caps headers not found])])
-
-AC_ARG_ENABLE(man,
-              [AS_HELP_STRING([--enable-man],
-                              [generate man pages [default=auto]])],,
-              enable_man=maybe)
-
-AS_IF([test "$enable_man" != no], [
-  AC_PATH_PROG([XSLTPROC], [xsltproc], [])
-  AS_IF([test -z "$XSLTPROC"], [
-    AS_IF([test "$enable_man" = yes], [
-      AC_MSG_ERROR([xsltproc is required for --enable-man])
-    ])
-    enable_man=no
-  ], [
-    enable_man=yes
-  ])
-])
-AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
-
-AC_ARG_WITH([bash-completion-dir],
-    AS_HELP_STRING([--with-bash-completion-dir[=PATH]],
-        [Install the bash auto-completion script in this directory. @<:@default=yes@:>@]),
-    [],
-    [with_bash_completion_dir=yes])
-
-AS_IF([test "x$with_bash_completion_dir" = "xyes"],
-  [
-    PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0],
-        [BASH_COMPLETION_DIR="`pkg-config --variable=completionsdir bash-completion`"],
-        [BASH_COMPLETION_DIR="$datadir/bash-completion/completions"])
-  ],
-  [
-    BASH_COMPLETION_DIR="$with_bash_completion_dir"
-  ])
-
-AC_SUBST([BASH_COMPLETION_DIR])
-AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"])
-
-AC_ARG_WITH([zsh-completion-dir],
-    AS_HELP_STRING([--with-zsh-completion-dir[=PATH]],
-        [Install the zsh auto-completion script in this directory. @<:@default=yes@:>@]),
-    [],
-    [with_zsh_completion_dir=yes])
-
-AS_IF([test "x$with_zsh_completion_dir" = "xyes"],
-  [ZSH_COMPLETION_DIR="$datadir/zsh/site-functions"],
-  [ZSH_COMPLETION_DIR="$with_zsh_completion_dir"])
-
-
-AC_SUBST([ZSH_COMPLETION_DIR])
-AM_CONDITIONAL([ENABLE_ZSH_COMPLETION], [test "x$with_zsh_completion_dir" != "xno"])
-
-# ------------------------------------------------------------------------------
-have_selinux=no
-AC_ARG_ENABLE(selinux, AS_HELP_STRING([--disable-selinux], [Disable optional SELINUX support]))
-AS_IF([test "x$enable_selinux" != "xno"], [
-        PKG_CHECK_MODULES([SELINUX], [libselinux >= 2.1.9],
-                [AC_DEFINE(HAVE_SELINUX, 1, [Define if SELinux is available])
-                 have_selinux=yes
-                 M4_DEFINES="$M4_DEFINES -DHAVE_SELINUX"],
-                [have_selinux=no])
-        AS_IF([test "x$have_selinux" = xno && test "x$enable_selinux" = xyes],
-          [AC_MSG_ERROR([*** SELinux support requested but libraries not found])])
-        PKG_CHECK_MODULES([SELINUX_2_3], [libselinux >= 2.3],
-                [AC_DEFINE(HAVE_SELINUX_2_3, 1, [Define if SELinux is version >= 2.3])],
-                [:])
-])
-AM_CONDITIONAL(HAVE_SELINUX, [test "$have_selinux" = "yes"])
-
-dnl Keep this in sync with ostree, except remove -Werror=declaration-after-statement
-CC_CHECK_FLAGS_APPEND([WARN_CFLAGS], [CFLAGS], [\
-        -pipe \
-        -Wall \
-        -Werror=shadow \
-	-Werror=empty-body \
-	-Werror=strict-prototypes \
-	-Werror=missing-prototypes \
-	-Werror=implicit-function-declaration \
-        "-Werror=format=2 -Werror=format-security -Werror=format-nonliteral" \
-	-Werror=pointer-arith -Werror=init-self \
-        -Werror=missing-declarations \
-        -Werror=return-type \
-        -Werror=overflow \
-        -Werror=int-conversion \
-        -Werror=parenthesis \
-        -Werror=incompatible-pointer-types \
-        -Werror=misleading-indentation \
-	-Werror=missing-include-dirs -Werror=aggregate-return \
-        -Werror=switch-default \
-        -Wswitch-enum \
-])
-AC_SUBST(WARN_CFLAGS)
-
-AC_CHECK_LIB(cap, cap_from_text)
-
-AS_IF([test "$ac_cv_lib_cap_cap_from_text" != "yes"],
-  [AC_MSG_ERROR([*** libcap requested but not found])])
-
-AC_ARG_WITH(priv-mode,
-            AS_HELP_STRING([--with-priv-mode=setuid/none],
-                           [How to set privilege-raising during make install]),
-            [],
-            [with_priv_mode="none"])
-
-AM_CONDITIONAL(PRIV_MODE_SETUID, test "x$with_priv_mode" = "xsetuid")
-
-AC_ARG_ENABLE(sudo,
-              AS_HELP_STRING([--enable-sudo],[Use sudo to set privileged mode on binaries during install (only needed if --with-priv-mode used)]),
-              [SUDO_BIN="sudo"], [SUDO_BIN=""])
-AC_SUBST([SUDO_BIN])
-
-AC_ARG_ENABLE(require-userns,
-            AS_HELP_STRING([--enable-require-userns=yes/no (default no)],
-                           [Require user namespaces by default when installed suid]),
-            [],
-            [enable_require_userns="no"])
-
-AS_IF([ test "x$enable_require_userns" = "xyes" ], [
-        AC_DEFINE(ENABLE_REQUIRE_USERNS, 1, [Define if userns should be used by default in suid mode])
-     ])
-
-AC_PROG_AWK
-AC_REQUIRE_AUX_FILE([tap-driver.sh])
-
-AC_CONFIG_FILES([
-Makefile
-])
-AC_OUTPUT
-
-echo "
-    bubblewrap $VERSION
-    ===================
-
-    man pages (xsltproc):                         $enable_man
-    SELinux:                                      $have_selinux
-    setuid mode on make install:                  $with_priv_mode
-    require default userns:                       $enable_require_userns
-    mysteriously satisfying to pop:               yes"
-echo ""
diff --git a/subprojects/bubblewrap/git.mk b/subprojects/bubblewrap/git.mk
deleted file mode 100644
index 0a7311592fbf27d1fd7957b1d33aa147260d9c56..0000000000000000000000000000000000000000
--- a/subprojects/bubblewrap/git.mk
+++ /dev/null
@@ -1,348 +0,0 @@
-# git.mk, a small Makefile to autogenerate .gitignore files
-# for autotools-based projects.
-#
-# Copyright 2009, Red Hat, Inc.
-# Copyright 2010,2011,2012,2013 Behdad Esfahbod
-# Written by Behdad Esfahbod
-#
-# Copying and distribution of this file, with or without modification,
-# is permitted in any medium without royalty provided the copyright
-# notice and this notice are preserved.
-#
-# The latest version of this file can be downloaded from:
-GIT_MK_URL = https://raw.githubusercontent.com/behdad/git.mk/master/git.mk
-#
-# Bugs, etc, should be reported upstream at:
-#   https://github.com/behdad/git.mk
-#
-# To use in your project, import this file in your git repo's toplevel,
-# then do "make -f git.mk".  This modifies all Makefile.am files in
-# your project to -include git.mk.  Remember to add that line to new
-# Makefile.am files you create in your project, or just rerun the
-# "make -f git.mk".
-#
-# This enables automatic .gitignore generation.  If you need to ignore
-# more files, add them to the GITIGNOREFILES variable in your Makefile.am.
-# But think twice before doing that.  If a file has to be in .gitignore,
-# chances are very high that it's a generated file and should be in one
-# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES.
-#
-# The only case that you need to manually add a file to GITIGNOREFILES is
-# when remove files in one of mostlyclean-local, clean-local, distclean-local,
-# or maintainer-clean-local make targets.
-#
-# Note that for files like editor backup, etc, there are better places to
-# ignore them.  See "man gitignore".
-#
-# If "make maintainer-clean" removes the files but they are not recognized
-# by this script (that is, if "git status" shows untracked files still), send
-# me the output of "git status" as well as your Makefile.am and Makefile for
-# the directories involved and I'll diagnose.
-#
-# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see
-# Makefile.am.sample in the git.mk git repo.
-#
-# Don't EXTRA_DIST this file.  It is supposed to only live in git clones,
-# not tarballs.  It serves no useful purpose in tarballs and clutters the
-# build dir.
-#
-# This file knows how to handle autoconf, automake, libtool, gtk-doc,
-# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata,
-# appstream.
-#
-# This makefile provides the following targets:
-#
-# - all: "make all" will build all gitignore files.
-# - gitignore: makes all gitignore files in the current dir and subdirs.
-# - .gitignore: make gitignore file for the current dir.
-# - gitignore-recurse: makes all gitignore files in the subdirs.
-#
-# KNOWN ISSUES:
-#
-# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the
-#   submodule doesn't find us.  If you have configure.{in,ac} files in
-#   subdirs, add a proxy git.mk file in those dirs that simply does:
-#   "include $(top_srcdir)/../git.mk".  Add more ..'s to your taste.
-#   And add those files to git.  See vte/gnome-pty-helper/git.mk for
-#   example.
-#
-
-
-
-###############################################################################
-# Variables user modules may want to add to toplevel MAINTAINERCLEANFILES:
-###############################################################################
-
-#
-# Most autotools-using modules should be fine including this variable in their
-# toplevel MAINTAINERCLEANFILES:
-GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \
-	$(srcdir)/aclocal.m4 \
-	$(srcdir)/autoscan.log \
-	$(srcdir)/configure.scan \
-	`AUX_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_AUX_DIR:$$1' ./configure.ac); \
-	 test "x$$AUX_DIR" = "x$(srcdir)/" && AUX_DIR=$(srcdir); \
-	 for x in \
-		ar-lib \
-		compile \
-		config.guess \
-		config.sub \
-		depcomp \
-		install-sh \
-		ltmain.sh \
-		missing \
-		mkinstalldirs \
-		test-driver \
-		ylwrap \
-	 ; do echo "$$AUX_DIR/$$x"; done` \
-	`cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_HEADERS:$$1' ./configure.ac | \
-	head -n 1 | while read f; do echo "$(srcdir)/$$f.in"; done`
-#
-# All modules should also be fine including the following variable, which
-# removes automake-generated Makefile.in files:
-GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \
-	`cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' ./configure.ac | \
-	while read f; do \
-	  case $$f in Makefile|*/Makefile) \
-	    test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \
-	done`
-#
-# Modules that use libtool and use  AC_CONFIG_MACRO_DIR() may also include this,
-# though it's harmless to include regardless.
-GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \
-	`MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \
-	 if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \
-		for x in \
-			libtool.m4 \
-			ltoptions.m4 \
-			ltsugar.m4 \
-			ltversion.m4 \
-			lt~obsolete.m4 \
-		; do echo "$$MACRO_DIR/$$x"; done; \
-	 fi`
-
-
-
-###############################################################################
-# Default rule is to install ourselves in all Makefile.am files:
-###############################################################################
-
-git-all: git-mk-install
-
-git-mk-install:
-	@echo "Installing git makefile"
-	@any_failed=; \
-		find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \
-		if grep 'include .*/git.mk' $$x >/dev/null; then \
-			echo "$$x already includes git.mk"; \
-		else \
-			failed=; \
-			echo "Updating $$x"; \
-			{ cat $$x; \
-			  echo ''; \
-			  echo '-include $$(top_srcdir)/git.mk'; \
-			} > $$x.tmp || failed=1; \
-			if test x$$failed = x; then \
-				mv $$x.tmp $$x || failed=1; \
-			fi; \
-			if test x$$failed = x; then : else \
-				echo "Failed updating $$x"; >&2 \
-				any_failed=1; \
-			fi; \
-	fi; done; test -z "$$any_failed"
-
-git-mk-update:
-	wget $(GIT_MK_URL) -O $(top_srcdir)/git.mk
-
-.PHONY: git-all git-mk-install git-mk-update
-
-
-
-###############################################################################
-# Actual .gitignore generation:
-###############################################################################
-
-$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
-	@echo "git.mk: Generating $@"
-	@{ \
-		if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \
-			for x in \
-				$(DOC_MODULE)-decl-list.txt \
-				$(DOC_MODULE)-decl.txt \
-				tmpl/$(DOC_MODULE)-unused.sgml \
-				"tmpl/*.bak" \
-				$(REPORT_FILES) \
-				$(DOC_MODULE).pdf \
-				xml html \
-			; do echo "/$$x"; done; \
-			FLAVOR=$$(cd $(top_srcdir); $(AUTOCONF) --trace 'GTK_DOC_CHECK:$$2' ./configure.ac); \
-			case $$FLAVOR in *no-tmpl*) echo /tmpl;; esac; \
-			if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-types"; then \
-				echo "/$(DOC_MODULE).types"; \
-			fi; \
-			if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-sections"; then \
-				echo "/$(DOC_MODULE)-sections.txt"; \
-			fi; \
-			if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \
-				for x in \
-					$(SETUP_FILES) \
-					$(DOC_MODULE).types \
-				; do echo "/$$x"; done; \
-			fi; \
-		fi; \
-		if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \
-			for lc in $(DOC_LINGUAS); do \
-				for x in \
-					$(if $(DOC_MODULE),$(DOC_MODULE).xml) \
-					$(DOC_PAGES) \
-					$(DOC_INCLUDES) \
-				; do echo "/$$lc/$$x"; done; \
-			done; \
-			for x in \
-				$(_DOC_OMF_ALL) \
-				$(_DOC_DSK_ALL) \
-				$(_DOC_HTML_ALL) \
-				$(_DOC_MOFILES) \
-				$(DOC_H_FILE) \
-				"*/.xml2po.mo" \
-				"*/*.omf.out" \
-			; do echo /$$x; done; \
-		fi; \
-		if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \
-			for lc in $(HELP_LINGUAS); do \
-				for x in \
-					$(HELP_FILES) \
-					"$$lc.stamp" \
-					"$$lc.mo" \
-				; do echo "/$$lc/$$x"; done; \
-			done; \
-		fi; \
-		if test "x$(gsettings_SCHEMAS)" = x; then :; else \
-			for x in \
-				$(gsettings_SCHEMAS:.xml=.valid) \
-				$(gsettings__enum_file) \
-			; do echo "/$$x"; done; \
-		fi; \
-		if test "x$(appdata_XML)" = x; then :; else \
-			for x in \
-				$(appdata_XML:.xml=.valid) \
-			; do echo "/$$x"; done; \
-		fi; \
-		if test "x$(appstream_XML)" = x; then :; else \
-			for x in \
-				$(appstream_XML:.xml=.valid) \
-			; do echo "/$$x"; done; \
-		fi; \
-		if test -f $(srcdir)/po/Makefile.in.in; then \
-			for x in \
-				po/Makefile.in.in \
-				po/Makefile.in.in~ \
-				po/Makefile.in \
-				po/Makefile \
-				po/Makevars.template \
-				po/POTFILES \
-				po/Rules-quot \
-				po/stamp-it \
-				po/stamp-po \
-				po/.intltool-merge-cache \
-				"po/*.gmo" \
-				"po/*.header" \
-				"po/*.mo" \
-				"po/*.sed" \
-				"po/*.sin" \
-				po/$(GETTEXT_PACKAGE).pot \
-				intltool-extract.in \
-				intltool-merge.in \
-				intltool-update.in \
-			; do echo "/$$x"; done; \
-		fi; \
-		if test -f $(srcdir)/configure; then \
-			for x in \
-				autom4te.cache \
-				configure \
-				config.h \
-				stamp-h1 \
-				libtool \
-				config.lt \
-			; do echo "/$$x"; done; \
-		fi; \
-		if test "x$(DEJATOOL)" = x; then :; else \
-			for x in \
-				$(DEJATOOL) \
-			; do echo "/$$x.sum"; echo "/$$x.log"; done; \
-			echo /site.exp; \
-		fi; \
-		if test "x$(am__dirstamp)" = x; then :; else \
-			echo "$(am__dirstamp)"; \
-		fi; \
-		if test "x$(LTCOMPILE)" = x -a "x$(LTCXXCOMPILE)" = x -a "x$(GTKDOC_RUN)" = x; then :; else \
-			for x in \
-				"*.lo" \
-				".libs" "_libs" \
-			; do echo "$$x"; done; \
-		fi; \
-		for x in \
-			.gitignore \
-			$(GITIGNOREFILES) \
-			$(CLEANFILES) \
-			$(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \
-			$(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \
-			$(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \
-			so_locations \
-			$(MOSTLYCLEANFILES) \
-			$(TEST_LOGS) \
-			$(TEST_LOGS:.log=.trs) \
-			$(TEST_SUITE_LOG) \
-			$(TESTS:=.test) \
-			"*.gcda" \
-			"*.gcno" \
-			$(DISTCLEANFILES) \
-			$(am__CONFIG_DISTCLEAN_FILES) \
-			$(CONFIG_CLEAN_FILES) \
-			TAGS ID GTAGS GRTAGS GSYMS GPATH tags \
-			"*.tab.c" \
-			$(MAINTAINERCLEANFILES) \
-			$(BUILT_SOURCES) \
-			$(patsubst %.vala,%.c,$(filter %.vala,$(SOURCES))) \
-			$(filter %_vala.stamp,$(DIST_COMMON)) \
-			$(filter %.vapi,$(DIST_COMMON)) \
-			$(filter $(addprefix %,$(notdir $(patsubst %.vapi,%.h,$(filter %.vapi,$(DIST_COMMON))))),$(DIST_COMMON)) \
-			Makefile \
-			Makefile.in \
-			"*.orig" \
-			"*.rej" \
-			"*.bak" \
-			"*~" \
-			".*.sw[nop]" \
-			".dirstamp" \
-		; do echo "/$$x"; done; \
-		for x in \
-			"*.$(OBJEXT)" \
-			$(DEPDIR) \
-		; do echo "$$x"; done; \
-	} | \
-	sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \
-	sed 's@/[.]/@/@g' | \
-	LC_ALL=C sort | uniq > $@.tmp && \
-	mv $@.tmp $@;
-
-all: $(srcdir)/.gitignore gitignore-recurse-maybe
-gitignore: $(srcdir)/.gitignore gitignore-recurse
-
-gitignore-recurse-maybe:
-	@for subdir in $(DIST_SUBDIRS); do \
-	  case " $(SUBDIRS) " in \
-	    *" $$subdir "*) :;; \
-	    *) test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir");; \
-	  esac; \
-	done
-gitignore-recurse:
-	@for subdir in $(DIST_SUBDIRS); do \
-	    test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir"); \
-	done
-
-maintainer-clean: gitignore-clean
-gitignore-clean:
-	-rm -f $(srcdir)/.gitignore
-
-.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe
diff --git a/subprojects/bubblewrap/m4/attributes.m4 b/subprojects/bubblewrap/m4/attributes.m4
deleted file mode 100644
index 51ac88be61875444b52737cf85141d8de2255adf..0000000000000000000000000000000000000000
--- a/subprojects/bubblewrap/m4/attributes.m4
+++ /dev/null
@@ -1,292 +0,0 @@
-dnl Macros to check the presence of generic (non-typed) symbols.
-dnl Copyright (c) 2006-2008 Diego Pettenò <flameeyes@gmail.com>
-dnl Copyright (c) 2006-2008 xine project
-dnl Copyright (c) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
-dnl
-dnl This program is free software; you can redistribute it and/or modify
-dnl it under the terms of the GNU General Public License as published by
-dnl the Free Software Foundation; either version 2, or (at your option)
-dnl any later version.
-dnl
-dnl This program is distributed in the hope that it will be useful,
-dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
-dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-dnl GNU General Public License for more details.
-dnl
-dnl You should have received a copy of the GNU General Public License
-dnl along with this program; if not, write to the Free Software
-dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-dnl 02110-1301, USA.
-dnl
-dnl As a special exception, the copyright owners of the
-dnl macro gives unlimited permission to copy, distribute and modify the
-dnl configure scripts that are the output of Autoconf when processing the
-dnl Macro. You need not follow the terms of the GNU General Public
-dnl License when using or distributing such scripts, even though portions
-dnl of the text of the Macro appear in them. The GNU General Public
-dnl License (GPL) does govern all other use of the material that
-dnl constitutes the Autoconf Macro.
-dnl
-dnl This special exception to the GPL applies to versions of the
-dnl Autoconf Macro released by this project. When you make and
-dnl distribute a modified version of the Autoconf Macro, you may extend
-dnl this special exception to the GPL to apply to your modified version as
-dnl well.
-
-dnl Check if FLAG in ENV-VAR is supported by compiler and append it
-dnl to WHERE-TO-APPEND variable. Note that we invert -Wno-* checks to
-dnl -W* as gcc cannot test for negated warnings. If a C snippet is passed,
-dnl use it, otherwise use a simple main() definition that just returns 0.
-dnl CC_CHECK_FLAG_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG], [C-SNIPPET])
-
-AC_DEFUN([CC_CHECK_FLAG_APPEND], [
-  AC_CACHE_CHECK([if $CC supports flag $3 in envvar $2],
-                 AS_TR_SH([cc_cv_$2_$3]),
-          [eval "AS_TR_SH([cc_save_$2])='${$2}'"
-           eval "AS_TR_SH([$2])='${cc_save_$2} -Werror `echo "$3" | sed 's/^-Wno-/-W/'`'"
-           AC_LINK_IFELSE([AC_LANG_SOURCE(ifelse([$4], [],
-                                                 [int main(void) { return 0; } ],
-                                                 [$4]))],
-                          [eval "AS_TR_SH([cc_cv_$2_$3])='yes'"],
-                          [eval "AS_TR_SH([cc_cv_$2_$3])='no'"])
-           eval "AS_TR_SH([$2])='$cc_save_$2'"])
-
-  AS_IF([eval test x$]AS_TR_SH([cc_cv_$2_$3])[ = xyes],
-        [eval "$1='${$1} $3'"])
-])
-
-dnl CC_CHECK_FLAGS_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG1 FLAG2], [C-SNIPPET])
-AC_DEFUN([CC_CHECK_FLAGS_APPEND], [
-  for flag in [$3]; do
-    CC_CHECK_FLAG_APPEND([$1], [$2], $flag, [$4])
-  done
-])
-
-dnl Check if the flag is supported by linker (cacheable)
-dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
-
-AC_DEFUN([CC_CHECK_LDFLAGS], [
-  AC_CACHE_CHECK([if $CC supports $1 flag],
-    AS_TR_SH([cc_cv_ldflags_$1]),
-    [ac_save_LDFLAGS="$LDFLAGS"
-     LDFLAGS="$LDFLAGS $1"
-     AC_LINK_IFELSE([int main() { return 1; }],
-       [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"],
-       [eval "AS_TR_SH([cc_cv_ldflags_$1])="])
-     LDFLAGS="$ac_save_LDFLAGS"
-    ])
-
-  AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes],
-    [$2], [$3])
-])
-
-dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for
-dnl the current linker to avoid undefined references in a shared object.
-AC_DEFUN([CC_NOUNDEFINED], [
-  dnl We check $host for which systems to enable this for.
-  AC_REQUIRE([AC_CANONICAL_HOST])
-
-  case $host in
-     dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads
-     dnl are requested, as different implementations are present; to avoid problems
-     dnl use -Wl,-z,defs only for those platform not behaving this way.
-     *-freebsd* | *-openbsd*) ;;
-     *)
-        dnl First of all check for the --no-undefined variant of GNU ld. This allows
-        dnl for a much more readable command line, so that people can understand what
-        dnl it does without going to look for what the heck -z defs does.
-        for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do
-           CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"])
-           break
-        done
-     ;;
-  esac
-
-  AC_SUBST([LDFLAGS_NOUNDEFINED])
-])
-
-dnl Check for a -Werror flag or equivalent. -Werror is the GCC
-dnl and ICC flag that tells the compiler to treat all the warnings
-dnl as fatal. We usually need this option to make sure that some
-dnl constructs (like attributes) are not simply ignored.
-dnl
-dnl Other compilers don't support -Werror per se, but they support
-dnl an equivalent flag:
-dnl  - Sun Studio compiler supports -errwarn=%all
-AC_DEFUN([CC_CHECK_WERROR], [
-  AC_CACHE_CHECK(
-    [for $CC way to treat warnings as errors],
-    [cc_cv_werror],
-    [CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror],
-      [CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])])
-    ])
-])
-
-AC_DEFUN([CC_CHECK_ATTRIBUTE], [
-  AC_REQUIRE([CC_CHECK_WERROR])
-  AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))],
-    AS_TR_SH([cc_cv_attribute_$1]),
-    [ac_save_CFLAGS="$CFLAGS"
-     CFLAGS="$CFLAGS $cc_cv_werror"
-     AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])],
-       [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"],
-       [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"])
-     CFLAGS="$ac_save_CFLAGS"
-    ])
-
-  AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes],
-    [AC_DEFINE(
-       AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1,
-         [Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))]
-         )
-     $4],
-    [$5])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [
-  CC_CHECK_ATTRIBUTE(
-    [constructor],,
-    [void __attribute__((constructor)) ctor() { int a; }],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_FORMAT], [
-  CC_CHECK_ATTRIBUTE(
-    [format], [format(printf, n, n)],
-    [void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [
-  CC_CHECK_ATTRIBUTE(
-    [format_arg], [format_arg(printf)],
-    [char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [
-  CC_CHECK_ATTRIBUTE(
-    [visibility_$1], [visibility("$1")],
-    [void __attribute__((visibility("$1"))) $1_function() { }],
-    [$2], [$3])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_NONNULL], [
-  CC_CHECK_ATTRIBUTE(
-    [nonnull], [nonnull()],
-    [void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_UNUSED], [
-  CC_CHECK_ATTRIBUTE(
-    [unused], ,
-    [void some_function(void *foo, __attribute__((unused)) void *bar);],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [
-  CC_CHECK_ATTRIBUTE(
-    [sentinel], ,
-    [void some_function(void *foo, ...) __attribute__((sentinel));],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [
-  CC_CHECK_ATTRIBUTE(
-    [deprecated], ,
-    [void some_function(void *foo, ...) __attribute__((deprecated));],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_ALIAS], [
-  CC_CHECK_ATTRIBUTE(
-    [alias], [weak, alias],
-    [void other_function(void *foo) { }
-     void some_function(void *foo) __attribute__((weak, alias("other_function")));],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_MALLOC], [
-  CC_CHECK_ATTRIBUTE(
-    [malloc], ,
-    [void * __attribute__((malloc)) my_alloc(int n);],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_PACKED], [
-  CC_CHECK_ATTRIBUTE(
-    [packed], ,
-    [struct astructure { char a; int b; long c; void *d; } __attribute__((packed));],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_CONST], [
-  CC_CHECK_ATTRIBUTE(
-    [const], ,
-    [int __attribute__((const)) twopow(int n) { return 1 << n; } ],
-    [$1], [$2])
-])
-
-AC_DEFUN([CC_FLAG_VISIBILITY], [
-  AC_REQUIRE([CC_CHECK_WERROR])
-  AC_CACHE_CHECK([if $CC supports -fvisibility=hidden],
-    [cc_cv_flag_visibility],
-    [cc_flag_visibility_save_CFLAGS="$CFLAGS"
-     CFLAGS="$CFLAGS $cc_cv_werror"
-     CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden],
-     cc_cv_flag_visibility='yes',
-     cc_cv_flag_visibility='no')
-     CFLAGS="$cc_flag_visibility_save_CFLAGS"])
-
-  AS_IF([test "x$cc_cv_flag_visibility" = "xyes"],
-    [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1,
-       [Define this if the compiler supports the -fvisibility flag])
-     $1],
-    [$2])
-])
-
-AC_DEFUN([CC_FUNC_EXPECT], [
-  AC_REQUIRE([CC_CHECK_WERROR])
-  AC_CACHE_CHECK([if compiler has __builtin_expect function],
-    [cc_cv_func_expect],
-    [ac_save_CFLAGS="$CFLAGS"
-     CFLAGS="$CFLAGS $cc_cv_werror"
-     AC_COMPILE_IFELSE([AC_LANG_SOURCE(
-       [int some_function() {
-        int a = 3;
-        return (int)__builtin_expect(a, 3);
-     }])],
-       [cc_cv_func_expect=yes],
-       [cc_cv_func_expect=no])
-     CFLAGS="$ac_save_CFLAGS"
-    ])
-
-  AS_IF([test "x$cc_cv_func_expect" = "xyes"],
-    [AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1,
-     [Define this if the compiler supports __builtin_expect() function])
-     $1],
-    [$2])
-])
-
-AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [
-  AC_REQUIRE([CC_CHECK_WERROR])
-  AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported],
-    [cc_cv_attribute_aligned],
-    [ac_save_CFLAGS="$CFLAGS"
-     CFLAGS="$CFLAGS $cc_cv_werror"
-     for cc_attribute_align_try in 64 32 16 8 4 2; do
-        AC_COMPILE_IFELSE([AC_LANG_SOURCE([
-          int main() {
-            static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0;
-            return c;
-          }])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
-     done
-     CFLAGS="$ac_save_CFLAGS"
-  ])
-
-  if test "x$cc_cv_attribute_aligned" != "x"; then
-     AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned],
-       [Define the highest alignment supported])
-  fi
-])
diff --git a/subprojects/bubblewrap/meson.build b/subprojects/bubblewrap/meson.build
index fb3af4ec44d85df22cb800ea2d1d25304e102f37..78678d0973035a04cb77635d34e576fc5e6bc236 100644
--- a/subprojects/bubblewrap/meson.build
+++ b/subprojects/bubblewrap/meson.build
@@ -1,7 +1,7 @@
 project(
   'bubblewrap',
   'c',
-  version : '0.10.0',
+  version : '0.11.0',
   meson_version : '>=0.49.0',
   default_options : [
     'warning_level=2',
diff --git a/subprojects/bubblewrap/meson_options.txt b/subprojects/bubblewrap/meson_options.txt
index 10a0a200203155afc83a427e0cc3dd929296893a..5e25ee86f86ba2c31a61ba10de9402acd3c956d6 100644
--- a/subprojects/bubblewrap/meson_options.txt
+++ b/subprojects/bubblewrap/meson_options.txt
@@ -45,7 +45,7 @@ option(
   'require_userns',
   type : 'boolean',
   description : 'require user namespaces by default when installed setuid',
-  value : 'false',
+  value : false,
 )
 option(
   'selinux',
@@ -57,7 +57,7 @@ option(
   'tests',
   type : 'boolean',
   description : 'build tests',
-  value : 'true',
+  value : true,
 )
 option(
   'zsh_completion',
diff --git a/subprojects/bubblewrap/network.c b/subprojects/bubblewrap/network.c
index f6d58a6c0137e6c5c6873295cca808605221b1d5..373d606a0abe3435fd5909ba8910e25fab4140be 100644
--- a/subprojects/bubblewrap/network.c
+++ b/subprojects/bubblewrap/network.c
@@ -53,8 +53,8 @@ rtnl_send_request (int              rtnl_fd,
   struct sockaddr_nl dst_addr = { AF_NETLINK, 0 };
   ssize_t sent;
 
-  sent = sendto (rtnl_fd, (void *) header, header->nlmsg_len, 0,
-                 (struct sockaddr *) &dst_addr, sizeof (dst_addr));
+  sent = TEMP_FAILURE_RETRY (sendto (rtnl_fd, (void *) header, header->nlmsg_len, 0,
+                                     (struct sockaddr *) &dst_addr, sizeof (dst_addr)));
   if (sent < 0)
     return -1;
 
@@ -71,7 +71,7 @@ rtnl_read_reply (int          rtnl_fd,
 
   while (1)
     {
-      received = recv (rtnl_fd, buffer, sizeof (buffer), 0);
+      received = TEMP_FAILURE_RETRY (recv (rtnl_fd, buffer, sizeof (buffer), 0));
       if (received < 0)
         return -1;
 
diff --git a/subprojects/bubblewrap/release-checklist.md b/subprojects/bubblewrap/release-checklist.md
index 4385b09bde9ebdfc943d707db68412ac614d25b9..5b2119c8af251dbba456b1df4d840d10a1b1a82a 100644
--- a/subprojects/bubblewrap/release-checklist.md
+++ b/subprojects/bubblewrap/release-checklist.md
@@ -1,13 +1,13 @@
 bubblewrap release checklist
 ============================
 
-* Collect release notes
-* Update version number in `configure.ac` **and** `meson.build`
+* Collect release notes in `NEWS`
+* Update version number in `meson.build` and release date in `NEWS`
 * Commit the changes
 * `meson dist -C ${builddir}`
 * Do any final smoke-testing, e.g. update a package, install and test it
 * `git evtag sign v$VERSION`
-    * Include the release notes in the tag message
+    * Include the release notes from `NEWS` in the tag message
 * `git push --atomic origin main v$VERSION`
 * https://github.com/containers/bubblewrap/releases/new
     * Fill in the new version's tag in the "Tag version" box
diff --git a/subprojects/bubblewrap/tests/test-run.sh b/subprojects/bubblewrap/tests/test-run.sh
index 8d063b57ced0c078853e6c553cb8e32e55d452c1..1003e8c4b1d593dae19cae1d55239085ae112780 100755
--- a/subprojects/bubblewrap/tests/test-run.sh
+++ b/subprojects/bubblewrap/tests/test-run.sh
@@ -109,7 +109,7 @@ assert_file_has_content json-status.json '"child-pid": [0-9]'
 assert_file_has_content_literal json-status.json '"exit-code": 42'
 ok "info and json-status fd"
 
-DATA=$($RUN --proc /proc --unshare-all --info-fd 42 --json-status-fd 43 -- bash -c 'stat -L --format "%n %i" /proc/self/ns/*' 42>info.json 43>json-status.json 2>err.txt)
+DATA=$($RUN --proc /proc --unshare-all --info-fd 42 --json-status-fd 43 -- bash -c 'stat -L -c "%n %i" /proc/self/ns/*' 42>info.json 43>json-status.json 2>err.txt)
 
 for NS in "ipc" "mnt" "net" "pid" "uts"; do
 
@@ -571,4 +571,122 @@ assert_file_has_content stdout foobar
 
 ok "bind-fd"
 
+$RUN --chdir / --chdir / true > stdout 2>&1
+assert_file_has_content stdout '^bwrap: Only the last --chdir option will take effect$'
+ok "warning logged for redundant --chdir"
+
+$RUN --level-prefix --chdir / --chdir / true > stdout 2>&1
+assert_file_has_content stdout '^<4>bwrap: Only the last --chdir option will take effect$'
+ok "--level-prefix"
+
+if test -n "${bwrap_is_suid:-}"; then
+    ok_skip "no --overlay support"
+    ok_skip "no --overlay support"
+    ok_skip "no --tmp-overlay support"
+    ok_skip "no --ro-overlay support"
+    ok_skip "no --overlay-src support"
+else
+    mkdir lower1 lower2 upper work
+    printf 1 > lower1/a
+    printf 2 > lower1/b
+    printf 3 > lower2/b
+    printf 4 > upper/a
+
+    # Check if unprivileged overlayfs is available
+    if ! unshare -rm mount -t overlay -o lowerdir=lower1,upperdir=upper,workdir=work,userxattr overlay lower2; then
+        ok_skip "no kernel support for unprivileged overlayfs"
+        ok_skip "no kernel support for unprivileged overlayfs"
+        ok_skip "no kernel support for unprivileged overlayfs"
+        ok_skip "no kernel support for unprivileged overlayfs"
+        ok_skip "no kernel support for unprivileged overlayfs"
+    else
+
+        # Test --overlay
+        if $RUN --overlay upper work /tmp true 2>err.txt; then
+            assert_not_reached At least one --overlay-src not required
+        fi
+        assert_file_has_content err.txt "^bwrap: --overlay requires at least one --overlay-src"
+        $RUN --overlay-src lower1 --overlay upper work /tmp/x/y/z cat /tmp/x/y/z/a > stdout
+        assert_file_has_content stdout '^4$'
+        $RUN --overlay-src lower1 --overlay upper work /tmp/x/y/z cat /tmp/x/y/z/b > stdout
+        assert_file_has_content stdout '^2$'
+        $RUN --overlay-src lower1 --overlay-src lower2 --overlay upper work /tmp/x/y/z cat /tmp/x/y/z/a > stdout
+        assert_file_has_content stdout '^4$'
+        $RUN --overlay-src lower1 --overlay-src lower2 --overlay upper work /tmp/x/y/z cat /tmp/x/y/z/b > stdout
+        assert_file_has_content stdout '^3$'
+        $RUN --overlay-src lower1 --overlay-src lower2 --overlay upper work /tmp/x/y/z sh -c 'printf 5 > /tmp/x/y/z/b; cat /tmp/x/y/z/b' > stdout
+        assert_file_has_content stdout '^5$'
+        assert_file_has_content upper/b '^5$'
+        ok "--overlay"
+
+        # Test --overlay path escaping
+        # Coincidentally, ":,\ is the face I make contemplating anyone who might
+        # need this functionality, not that that's going to stop me from supporting
+        # it.
+        mkdir 'lower ":,\' 'upper ":,\' 'work ":,\'
+        printf 1 > 'lower ":,\'/a
+        $RUN --overlay-src 'lower ":,\' --overlay 'upper ":,\' 'work ":,\' /tmp/x sh -c 'cat /tmp/x/a; printf 2 > /tmp/x/a; cat /tmp/x/a' > stdout
+        assert_file_has_content stdout '^12$'
+        assert_file_has_content 'lower ":,\'/a '^1$'
+        assert_file_has_content 'upper ":,\'/a '^2$'
+        ok "--overlay path escaping"
+
+        # Test --tmp-overlay
+        printf 1 > lower1/a
+        printf 2 > lower1/b
+        printf 3 > lower2/b
+        if $RUN --tmp-overlay /tmp true 2>err.txt; then
+            assert_not_reached At least one --overlay-src not required
+        fi
+        assert_file_has_content err.txt "^bwrap: --tmp-overlay requires at least one --overlay-src"
+        $RUN --overlay-src lower1 --tmp-overlay /tmp/x/y/z cat /tmp/x/y/z/a > stdout
+        assert_file_has_content stdout '^1$'
+        $RUN --overlay-src lower1 --tmp-overlay /tmp/x/y/z cat /tmp/x/y/z/b > stdout
+        assert_file_has_content stdout '^2$'
+        $RUN --overlay-src lower1 --overlay-src lower2 --tmp-overlay /tmp/x/y/z cat /tmp/x/y/z/a > stdout
+        assert_file_has_content stdout '^1$'
+        $RUN --overlay-src lower1 --overlay-src lower2 --tmp-overlay /tmp/x/y/z cat /tmp/x/y/z/b > stdout
+        assert_file_has_content stdout '^3$'
+        $RUN --overlay-src lower1 --overlay-src lower2 --tmp-overlay /tmp/x/y/z sh -c 'printf 4 > /tmp/x/y/z/b; cat /tmp/x/y/z/b' > stdout
+        assert_file_has_content stdout '^4$'
+        $RUN --overlay-src lower1 --tmp-overlay /tmp/x --overlay-src lower2 --tmp-overlay /tmp/y sh -c 'cat /tmp/x/b; printf 4 > /tmp/x/b; cat /tmp/x/b; cat /tmp/y/b' > stdout
+        assert_file_has_content stdout '^243$'
+        assert_file_has_content lower1/b '^2$'
+        assert_file_has_content lower2/b '^3$'
+        ok "--tmp-overlay"
+
+        # Test --ro-overlay
+        printf 1 > lower1/a
+        printf 2 > lower1/b
+        printf 3 > lower2/b
+        if $RUN --ro-overlay /tmp true 2>err.txt; then
+            assert_not_reached At least two --overlay-src not required
+        fi
+        assert_file_has_content err.txt "^bwrap: --ro-overlay requires at least two --overlay-src"
+        if $RUN --overlay-src lower1 --ro-overlay /tmp true 2>err.txt; then
+            assert_not_reached At least two --overlay-src not required
+        fi
+        assert_file_has_content err.txt "^bwrap: --ro-overlay requires at least two --overlay-src"
+        $RUN --overlay-src lower1 --overlay-src lower2 --ro-overlay /tmp/x/y/z cat /tmp/x/y/z/a > stdout
+        assert_file_has_content stdout '^1$'
+        $RUN --overlay-src lower1 --overlay-src lower2 --ro-overlay /tmp/x/y/z cat /tmp/x/y/z/b > stdout
+        assert_file_has_content stdout '^3$'
+        $RUN --overlay-src lower1 --overlay-src lower2 --ro-overlay /tmp/x/y/z sh -c 'printf 4 > /tmp/x/y/z/b; cat /tmp/x/y/z/b' > stdout
+        assert_file_has_content stdout '^3$'
+        ok "--ro-overlay"
+
+        # Test --overlay-src restrictions
+        if $RUN --overlay-src /tmp true 2>err.txt; then
+            assert_not_reached Trailing --overlay-src allowed
+        fi
+        assert_file_has_content err.txt "^bwrap: --overlay-src must be followed by another --overlay-src or one of --overlay, --tmp-overlay, or --ro-overlay"
+        if $RUN --overlay-src /tmp --chdir / true 2>err.txt; then
+            assert_not_reached --overlay-src allowed to precede non-overlay options
+        fi
+        assert_file_has_content err.txt "^bwrap: --overlay-src must be followed by another --overlay-src or one of --overlay, --tmp-overlay, or --ro-overlay"
+        ok "--overlay-src restrictions"
+
+    fi
+fi
+
 done_testing
diff --git a/subprojects/bubblewrap/tests/test-utils.c b/subprojects/bubblewrap/tests/test-utils.c
index 41874a15cbdb840bf122751592a987d799ecb4c4..64f42ff18c0dba3877e9bf2f3eb92b02a48f1a39 100644
--- a/subprojects/bubblewrap/tests/test-utils.c
+++ b/subprojects/bubblewrap/tests/test-utils.c
@@ -170,15 +170,15 @@ test_has_path_prefix (void)
     bool expected;
   } tests[] =
   {
-    { "/run/host/usr", "/run/host", TRUE },
-    { "/run/host/usr", "/run/host/", TRUE },
-    { "/run/host", "/run/host", TRUE },
-    { "////run///host////usr", "//run//host", TRUE },
-    { "////run///host////usr", "//run//host////", TRUE },
-    { "/run/hostage", "/run/host", FALSE },
+    { "/run/host/usr", "/run/host", true },
+    { "/run/host/usr", "/run/host/", true },
+    { "/run/host", "/run/host", true },
+    { "////run///host////usr", "//run//host", true },
+    { "////run///host////usr", "//run//host////", true },
+    { "/run/hostage", "/run/host", false },
     /* Any number of leading slashes is ignored, even zero */
-    { "foo/bar", "/foo", TRUE },
-    { "/foo/bar", "foo", TRUE },
+    { "foo/bar", "/foo", true },
+    { "/foo/bar", "foo", true },
   };
   size_t i;
 
@@ -200,6 +200,37 @@ test_has_path_prefix (void)
     }
 }
 
+static void
+test_string_builder (void)
+{
+  StringBuilder sb = {0};
+
+  strappend (&sb, "aaa");
+  g_assert_cmpstr (sb.str, ==, "aaa");
+  strappend (&sb, "bbb");
+  g_assert_cmpstr (sb.str, ==, "aaabbb");
+  strappendf (&sb, "c%dc%s", 9, "x");
+  g_assert_cmpstr (sb.str, ==, "aaabbbc9cx");
+  strappend_escape_for_mount_options (&sb, "/path :,\\");
+  g_assert_cmpstr (sb.str, ==, "aaabbbc9cx/path \\:\\,\\\\");
+  strappend (&sb, "zzz");
+  g_assert_cmpstr (sb.str, ==, "aaabbbc9cx/path \\:\\,\\\\zzz");
+
+  free (sb.str);
+  sb = (StringBuilder){0};
+
+  strappend_escape_for_mount_options (&sb, "aaa");
+  g_assert_cmpstr (sb.str, ==, "aaa");
+
+  free (sb.str);
+  sb = (StringBuilder){0};
+
+  strappend_escape_for_mount_options (&sb, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+  g_assert_cmpstr (sb.str, ==, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+
+  free (sb.str);
+}
+
 int
 main (int argc UNUSED,
       char **argv UNUSED)
@@ -210,6 +241,7 @@ main (int argc UNUSED,
   test_strconcat3 ();
   test_has_prefix ();
   test_has_path_prefix ();
+  test_string_builder ();
   printf ("1..%u\n", test_number);
   return 0;
 }
diff --git a/subprojects/bubblewrap/utils.c b/subprojects/bubblewrap/utils.c
index 43c8d798a9428536c509ed534e639dc0de442483..51875aea9a1a60dcdddb649b7d138f4cd76d93ce 100644
--- a/subprojects/bubblewrap/utils.c
+++ b/subprojects/bubblewrap/utils.c
@@ -23,6 +23,7 @@
 #include <stdint.h>
 #include <sys/syscall.h>
 #include <sys/socket.h>
+#include <sys/param.h>
 #ifdef HAVE_SELINUX
 #include <selinux/selinux.h>
 #endif
@@ -34,11 +35,17 @@
 #define security_check_context(x) security_check_context ((security_context_t) x)
 #endif
 
-__attribute__((format(printf, 1, 0))) static void
-warnv (const char *format,
-       va_list args,
-       const char *detail)
+bool bwrap_level_prefix = false;
+
+__attribute__((format(printf, 2, 0))) static void
+bwrap_logv (int severity,
+            const char *format,
+            va_list args,
+            const char *detail)
 {
+  if (bwrap_level_prefix)
+    fprintf (stderr, "<%d>", severity);
+
   fprintf (stderr, "bwrap: ");
   vfprintf (stderr, format, args);
 
@@ -49,12 +56,13 @@ warnv (const char *format,
 }
 
 void
-warn (const char *format, ...)
+bwrap_log (int severity,
+           const char *format, ...)
 {
   va_list args;
 
   va_start (args, format);
-  warnv (format, args, NULL);
+  bwrap_logv (severity, format, args, NULL);
   va_end (args);
 }
 
@@ -67,7 +75,7 @@ die_with_error (const char *format, ...)
   errsv = errno;
 
   va_start (args, format);
-  warnv (format, args, strerror (errsv));
+  bwrap_logv (LOG_ERR, format, args, strerror (errsv));
   va_end (args);
 
   exit (1);
@@ -82,7 +90,7 @@ die_with_mount_error (const char *format, ...)
   errsv = errno;
 
   va_start (args, format);
-  warnv (format, args, mount_strerror (errsv));
+  bwrap_logv (LOG_ERR, format, args, mount_strerror (errsv));
   va_end (args);
 
   exit (1);
@@ -94,7 +102,7 @@ die (const char *format, ...)
   va_list args;
 
   va_start (args, format);
-  warnv (format, args, NULL);
+  bwrap_logv (LOG_ERR, format, args, NULL);
   va_end (args);
 
   exit (1);
@@ -206,7 +214,7 @@ bool
 has_path_prefix (const char *str,
                  const char *prefix)
 {
-  while (TRUE)
+  while (true)
     {
       /* Skip consecutive slashes to reach next path
          element */
@@ -217,13 +225,13 @@ has_path_prefix (const char *str,
 
       /* No more prefix path elements? Done! */
       if (*prefix == 0)
-        return TRUE;
+        return true;
 
       /* Compare path element */
       while (*prefix != 0 && *prefix != '/')
         {
           if (*str != *prefix)
-            return FALSE;
+            return false;
           str++;
           prefix++;
         }
@@ -231,7 +239,7 @@ has_path_prefix (const char *str,
       /* Matched prefix path element,
          must be entire str path element */
       if (*str != '/' && *str != 0)
-        return FALSE;
+        return false;
     }
 }
 
@@ -239,7 +247,7 @@ bool
 path_equal (const char *path1,
             const char *path2)
 {
-  while (TRUE)
+  while (true)
     {
       /* Skip consecutive slashes to reach next path
          element */
@@ -256,14 +264,14 @@ path_equal (const char *path1,
       while (*path1 != 0 && *path1 != '/')
         {
           if (*path1 != *path2)
-            return FALSE;
+            return false;
           path1++;
           path2++;
         }
 
       /* Matched path1 path element, must be entire path element */
       if (*path2 != '/' && *path2 != 0)
-        return FALSE;
+        return false;
     }
 }
 
@@ -370,7 +378,7 @@ fdwalk (int proc_fd, int (*cb)(void *data,
   int res = 0;
   DIR *d;
 
-  dfd = openat (proc_fd, "self/fd", O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY);
+  dfd = TEMP_FAILURE_RETRY (openat (proc_fd, "self/fd", O_DIRECTORY | O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOCTTY));
   if (dfd == -1)
     return res;
 
@@ -444,7 +452,7 @@ write_to_fd (int         fd,
 
 /* Sets errno on error (!= 0), ENOSPC on short write */
 int
-write_file_at (int         dirfd,
+write_file_at (int         dfd,
                const char *path,
                const char *content)
 {
@@ -452,7 +460,7 @@ write_file_at (int         dirfd,
   bool res;
   int errsv;
 
-  fd = openat (dirfd, path, O_RDWR | O_CLOEXEC, 0);
+  fd = TEMP_FAILURE_RETRY (openat (dfd, path, O_RDWR | O_CLOEXEC, 0));
   if (fd == -1)
     return -1;
 
@@ -477,7 +485,7 @@ create_file (const char *path,
   int res;
   int errsv;
 
-  fd = creat (path, mode);
+  fd = TEMP_FAILURE_RETRY (creat (path, mode));
   if (fd == -1)
     return -1;
 
@@ -526,7 +534,7 @@ copy_file_data (int sfd,
   char buffer[BUFSIZE];
   ssize_t bytes_read;
 
-  while (TRUE)
+  while (true)
     {
       bytes_read = read (sfd, buffer, BUFSIZE);
       if (bytes_read == -1)
@@ -558,11 +566,11 @@ copy_file (const char *src_path,
   int res;
   int errsv;
 
-  sfd = open (src_path, O_CLOEXEC | O_RDONLY);
+  sfd = TEMP_FAILURE_RETRY (open (src_path, O_CLOEXEC | O_RDONLY));
   if (sfd == -1)
     return -1;
 
-  dfd = creat (dst_path, mode);
+  dfd = TEMP_FAILURE_RETRY (creat (dst_path, mode));
   if (dfd == -1)
     {
       errsv = errno;
@@ -632,14 +640,14 @@ load_file_data (int     fd,
 /* Sets errno on error (== NULL),
  * Always ensures terminating zero */
 char *
-load_file_at (int         dirfd,
+load_file_at (int         dfd,
               const char *path)
 {
   int fd;
   char *data;
   int errsv;
 
-  fd = openat (dirfd, path, O_CLOEXEC | O_RDONLY);
+  fd = TEMP_FAILURE_RETRY (openat (dfd, path, O_CLOEXEC | O_RDONLY));
   if (fd == -1)
     return NULL;
 
@@ -745,15 +753,15 @@ mkdir_with_parents (const char *pathname,
    read back with read_pid_from_socket(), and then the kernel has
    translated it between namespaces as needed. */
 void
-send_pid_on_socket (int socket)
+send_pid_on_socket (int sockfd)
 {
   char buf[1] = { 0 };
   struct msghdr msg = {};
   struct iovec iov = { buf, sizeof (buf) };
   const ssize_t control_len_snd = CMSG_SPACE(sizeof(struct ucred));
-  char control_buf_snd[control_len_snd];
+  _Alignas(struct cmsghdr) char control_buf_snd[control_len_snd];
   struct cmsghdr *cmsg;
-  struct ucred *cred;
+  struct ucred cred;
 
   msg.msg_iov = &iov;
   msg.msg_iovlen = 1;
@@ -764,13 +772,13 @@ send_pid_on_socket (int socket)
   cmsg->cmsg_level = SOL_SOCKET;
   cmsg->cmsg_type = SCM_CREDENTIALS;
   cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
-  cred = (struct ucred *)CMSG_DATA(cmsg);
 
-  cred->pid = getpid ();
-  cred->uid = geteuid ();
-  cred->gid = getegid ();
+  cred.pid = getpid ();
+  cred.uid = geteuid ();
+  cred.gid = getegid ();
+  memcpy (CMSG_DATA (cmsg), &cred, sizeof (cred));
 
-  if (sendmsg (socket, &msg, 0) < 0)
+  if (TEMP_FAILURE_RETRY (sendmsg (sockfd, &msg, 0)) < 0)
     die_with_error ("Can't send pid");
 }
 
@@ -787,13 +795,13 @@ create_pid_socketpair (int sockets[2])
 }
 
 int
-read_pid_from_socket (int socket)
+read_pid_from_socket (int sockfd)
 {
   char recv_buf[1] = { 0 };
   struct msghdr msg = {};
   struct iovec iov = { recv_buf, sizeof (recv_buf) };
   const ssize_t control_len_rcv = CMSG_SPACE(sizeof(struct ucred));
-  char control_buf_rcv[control_len_rcv];
+  _Alignas(struct cmsghdr) char control_buf_rcv[control_len_rcv];
   struct cmsghdr* cmsg;
 
   msg.msg_iov = &iov;
@@ -801,7 +809,7 @@ read_pid_from_socket (int socket)
   msg.msg_control = control_buf_rcv;
   msg.msg_controllen = control_len_rcv;
 
-  if (recvmsg (socket, &msg, 0) < 0)
+  if (TEMP_FAILURE_RETRY (recvmsg (sockfd, &msg, 0)) < 0)
     die_with_error ("Can't read pid from socket");
 
   if (msg.msg_controllen <= 0)
@@ -814,8 +822,10 @@ read_pid_from_socket (int socket)
           cmsg->cmsg_type == SCM_CREDENTIALS &&
           payload_len == sizeof(struct ucred))
         {
-          struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
-          return cred->pid;
+          struct ucred cred;
+
+          memcpy (&cred, CMSG_DATA (cmsg), sizeof (cred));
+          return cred.pid;
         }
     }
   die ("No pid returned on socket");
@@ -943,3 +953,128 @@ mount_strerror (int errsv)
         return strerror (errsv);
     }
 }
+
+/*
+ * Return a + b if it would not overflow.
+ * Die with an "out of memory" error if it would.
+ */
+static size_t
+xadd (size_t a, size_t b)
+{
+#if defined(__GNUC__) && __GNUC__ >= 5
+  size_t result;
+  if (__builtin_add_overflow (a, b, &result))
+    die_oom ();
+  return result;
+#else
+  if (a > SIZE_MAX - b)
+    die_oom ();
+
+  return a + b;
+#endif
+}
+
+/*
+ * Return a * b if it would not overflow.
+ * Die with an "out of memory" error if it would.
+ */
+static size_t
+xmul (size_t a, size_t b)
+{
+#if defined(__GNUC__) && __GNUC__ >= 5
+  size_t result;
+  if (__builtin_mul_overflow (a, b, &result))
+    die_oom ();
+  return result;
+#else
+  if (b != 0 && a > SIZE_MAX / b)
+    die_oom ();
+
+  return a * b;
+#endif
+}
+
+void
+strappend (StringBuilder *dest, const char *src)
+{
+  size_t len = strlen (src);
+  size_t new_offset = xadd (dest->offset, len);
+
+  if (new_offset >= dest->size)
+    {
+      dest->size = xmul (xadd (new_offset, 1), 2);
+      dest->str = xrealloc (dest->str, dest->size);
+    }
+
+  /* Preserves the invariant that dest->str is always null-terminated, even
+   * though the offset is positioned at the null byte for the next write.
+   */
+  strncpy (dest->str + dest->offset, src, len + 1);
+  dest->offset = new_offset;
+}
+
+__attribute__((format (printf, 2, 3)))
+void
+strappendf (StringBuilder *dest, const char *fmt, ...)
+{
+  va_list args;
+  int len;
+  size_t new_offset;
+
+  va_start (args, fmt);
+  len = vsnprintf (dest->str + dest->offset, dest->size - dest->offset, fmt, args);
+  va_end (args);
+  if (len < 0)
+    die_with_error ("vsnprintf");
+  new_offset = xadd (dest->offset, len);
+  if (new_offset >= dest->size)
+    {
+      dest->size = xmul (xadd (new_offset, 1), 2);
+      dest->str = xrealloc (dest->str, dest->size);
+      va_start (args, fmt);
+      len = vsnprintf (dest->str + dest->offset, dest->size - dest->offset, fmt, args);
+      va_end (args);
+      if (len < 0)
+        die_with_error ("vsnprintf");
+    }
+
+  dest->offset = new_offset;
+}
+
+void
+strappend_escape_for_mount_options (StringBuilder *dest, const char *src)
+{
+  bool unescaped = true;
+
+  for (;;)
+    {
+      if (dest->offset == dest->size)
+        {
+          dest->size = MAX (64, xmul (dest->size, 2));
+          dest->str = xrealloc (dest->str, dest->size);
+        }
+      switch (*src)
+        {
+        case '\0':
+          dest->str[dest->offset] = '\0';
+          return;
+
+        case '\\':
+        case ',':
+        case ':':
+          if (unescaped)
+            {
+              dest->str[dest->offset++] = '\\';
+              unescaped = false;
+              continue;
+            }
+          /* else fall through */
+
+        default:
+          dest->str[dest->offset++] = *src;
+          unescaped = true;
+          break;
+        }
+      src++;
+    }
+}
diff --git a/subprojects/bubblewrap/utils.h b/subprojects/bubblewrap/utils.h
index 9f17297d2a7c0b0bb0f4ba22468fffc25a6daab1..079fe7c9362e04b952544b1daab0331c2dbe16f1 100644
--- a/subprojects/bubblewrap/utils.h
+++ b/subprojects/bubblewrap/utils.h
@@ -24,26 +24,33 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <syslog.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #if 0
-#define __debug__(x) printf x
+#define debug(...) bwrap_log (LOG_DEBUG, __VA_ARGS__)
 #else
-#define __debug__(x)
+#define debug(...)
 #endif
 
 #define UNUSED __attribute__((__unused__))
 
 #define N_ELEMENTS(arr) (sizeof (arr) / sizeof ((arr)[0]))
 
-#define TRUE 1
-#define FALSE 0
-typedef int bool;
+#ifndef TEMP_FAILURE_RETRY
+#define TEMP_FAILURE_RETRY(expression) \
+  (__extension__                                                              \
+    ({ long int __result;                                                     \
+       do __result = (long int) (expression);                                 \
+       while (__result == -1L && errno == EINTR);                             \
+       __result; }))
+#endif
 
 #define PIPE_READ_END 0
 #define PIPE_WRITE_END 1
@@ -52,8 +59,13 @@ typedef int bool;
 #define PR_SET_CHILD_SUBREAPER 36
 #endif
 
-void  warn (const char *format,
-            ...) __attribute__((format (printf, 1, 2)));
+extern bool bwrap_level_prefix;
+
+void  bwrap_log (int severity,
+                 const char *format,
+                 ...) __attribute__((format (printf, 2, 3)));
+#define warn(...) bwrap_log (LOG_WARNING, __VA_ARGS__)
+
 void  die_with_error (const char *format,
                       ...) __attribute__((__noreturn__)) __attribute__((format (printf, 1, 2)));
 void  die_with_mount_error (const char *format,
@@ -186,3 +198,20 @@ steal_pointer (void *pp)
 /* type safety */
 #define steal_pointer(pp) \
   (0 ? (*(pp)) : (steal_pointer) (pp))
+
+typedef struct _StringBuilder StringBuilder;
+
+struct _StringBuilder
+{
+  char * str;
+  size_t size;
+  size_t offset;
+};
+
+void strappend (StringBuilder *dest,
+                const char    *src);
+void strappendf (StringBuilder *dest,
+                 const char    *fmt,
+                 ...);
+void strappend_escape_for_mount_options (StringBuilder *dest,
+                                         const char    *src);