diff --git a/src/tests/wayland-test-clients/meson.build b/src/tests/wayland-test-clients/meson.build
index 3d795dd0e..1091651f4 100644
--- a/src/tests/wayland-test-clients/meson.build
+++ b/src/tests/wayland-test-clients/meson.build
@@ -59,3 +59,19 @@ executable('subsurface-remap-toplevel',
install: have_installed_tests,
install_dir: wayland_test_client_installed_tests_libexecdir,
)
+
+executable('meta-anonymous-file',
+ sources: [
+ 'meta-anonymous-file.c',
+ common_sources,
+ ],
+ include_directories: tests_includepath,
+ c_args: tests_c_args,
+ dependencies: [
+ glib_dep,
+ wayland_client_dep,
+ libmutter_dep,
+ ],
+ install: have_installed_tests,
+ install_dir: wayland_test_client_installed_tests_libexecdir,
+)
diff --git a/src/tests/wayland-test-clients/meta-anonymous-file.c b/src/tests/wayland-test-clients/meta-anonymous-file.c
new file mode 100644
index 000000000..4bf0e2ee3
--- /dev/null
+++ b/src/tests/wayland-test-clients/meta-anonymous-file.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2020 Jonas Dreßler.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "wayland-test-client-utils.h"
+
+#define READONLY_SEALS (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
+
+static const char *teststring = "test string 1234567890";
+
+static int
+test_read_fd_mmap (int fd,
+ const char *expected_string)
+{
+ void *mem;
+ int string_size;
+
+ string_size = strlen (expected_string) + 1;
+
+ mem = mmap (NULL, string_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ g_assert (mem != MAP_FAILED);
+
+ if (strcmp (expected_string, mem) != 0)
+ {
+ munmap (mem, string_size);
+ return FALSE;
+ }
+
+ munmap (mem, string_size);
+ return TRUE;
+}
+
+static int
+test_write_fd (int fd,
+ const char *string)
+{
+ int written_size, string_size;
+
+ string_size = strlen (string) + 1;
+ written_size = write (fd, string, string_size);
+ if (written_size != string_size)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+test_readonly_seals (int fd)
+{
+ unsigned int seals;
+
+ seals = fcntl (fd, F_GET_SEALS);
+ if (seals == -1)
+ return FALSE;
+
+ if (seals != READONLY_SEALS)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+test_write_read (int fd)
+{
+ g_autofree char *new_string = g_uuid_string_random ();
+
+ if (!test_write_fd (fd, new_string))
+ return FALSE;
+
+ if (!test_read_fd_mmap (fd, new_string))
+ return FALSE;
+
+ return TRUE;
+}
+
+#if defined(HAVE_MEMFD_CREATE)
+static int
+test_open_write_read (const char *path)
+{
+ int fd;
+
+ fd = open (path, O_RDWR);
+ g_assert (fd != -1);
+
+ if (!test_write_read (fd))
+ {
+ close (fd);
+ return FALSE;
+ }
+
+ close (fd);
+ return TRUE;
+}
+#endif
+
+int
+main (int argc,
+ char **argv)
+{
+ MetaAnonymousFile *file;
+ int fd = -1, other_fd = -1;
+ g_autofree char *fd_path = NULL;
+
+ file = meta_anonymous_file_new (strlen (teststring) + 1,
+ (const uint8_t *) teststring);
+ if (!file)
+ {
+ g_critical ("%s: Creating file failed", __func__);
+ return EXIT_FAILURE;
+ }
+
+#if defined(HAVE_MEMFD_CREATE)
+ fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
+ g_assert (fd != -1);
+ other_fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
+ g_assert (other_fd != -1);
+
+ /* When MAPMODE_PRIVATE was used, meta_anonymous_file_open_fd() should always
+ * return the same fd. */
+ if (other_fd != fd)
+ goto fail;
+
+ /* If memfd_create was used and we request a MAPMODE_PRIVATE file, all the
+ * readonly seals should be set. */
+ if (!test_readonly_seals (fd))
+ goto fail;
+
+ if (!test_read_fd_mmap (fd, teststring))
+ goto fail;
+
+ /* Writing and reading the written data should fail */
+ if (test_write_read (fd))
+ goto fail;
+
+ /* Instead we should still be reading the teststring */
+ if (!test_read_fd_mmap (fd, teststring))
+ goto fail;
+
+ /* Opening the fd manually in RW mode and writing to it should fail */
+ fd_path = g_strdup_printf ("/proc/%d/fd/%d", getpid (), fd);
+ if (test_open_write_read (fd_path))
+ goto fail;
+
+ /* Instead we should still be reading the teststring */
+ if (!test_read_fd_mmap (fd, teststring))
+ goto fail;
+
+ /* Just to be sure test the other fd, too */
+ if (!test_read_fd_mmap (other_fd, teststring))
+ goto fail;
+
+ meta_anonymous_file_close_fd (fd);
+ meta_anonymous_file_close_fd (fd);
+
+
+ fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_SHARED);
+ g_assert (fd != -1);
+ other_fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_SHARED);
+ g_assert (other_fd != -1);
+
+ /* The MAPMODE_SHARED fd should not have readonly seals applied */
+ if (test_readonly_seals (fd))
+ goto fail;
+
+ if (!test_read_fd_mmap (fd, teststring))
+ goto fail;
+
+ if (!test_read_fd_mmap (other_fd, teststring))
+ goto fail;
+
+ /* Writing and reading the written data should succeed */
+ if (!test_write_read (fd))
+ goto fail;
+
+ /* The other fd should still read the teststring though */
+ if (!test_read_fd_mmap (other_fd, teststring))
+ goto fail;
+
+ meta_anonymous_file_close_fd (fd);
+ meta_anonymous_file_close_fd (other_fd);
+
+
+ /* Test an artificial out-of-space situation by setting the maximium file
+ * size this process may create to 2 bytes, if memfd_create with
+ * MAPMODE_PRIVATE is used, everything should still work (the existing FD
+ * should be used). */
+ struct rlimit limit = {2, 2};
+ if (setrlimit (RLIMIT_FSIZE, &limit) == -1)
+ goto fail;
+
+ fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
+ g_assert (fd != -1);
+
+ if (!test_read_fd_mmap (fd, teststring))
+ goto fail;
+
+ meta_anonymous_file_close_fd (fd);
+#else
+ fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
+ g_assert (fd != -1);
+ other_fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
+ g_assert (other_fd != -1);
+
+ if (test_readonly_seals (fd))
+ goto fail;
+
+ /* Writing and reading the written data should succeed */
+ if (!test_write_read (fd))
+ goto fail;
+
+ /* The other fd should still read the teststring though */
+ if (!test_read_fd_mmap (other_fd, teststring))
+ goto fail;
+
+ meta_anonymous_file_close_fd (fd);
+ meta_anonymous_file_close_fd (other_fd);
+
+
+ fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_SHARED);
+ g_assert (fd != -1);
+ other_fd = meta_anonymous_file_open_fd (file, META_ANONYMOUS_FILE_MAPMODE_SHARED);
+ g_assert (other_fd != -1);
+
+ if (test_readonly_seals (fd))
+ goto fail;
+
+ if (!test_read_fd_mmap (fd, teststring))
+ goto fail;
+
+ if (!test_read_fd_mmap (other_fd, teststring))
+ goto fail;
+
+ /* Writing and reading the written data should succeed */
+ if (!test_write_read (fd))
+ goto fail;
+
+ /* The other fd should still read the teststring though */
+ if (!test_read_fd_mmap (other_fd, teststring))
+ goto fail;
+
+ meta_anonymous_file_close_fd (fd);
+ meta_anonymous_file_close_fd (other_fd);
+#endif
+
+ meta_anonymous_file_free (file);
+ return EXIT_SUCCESS;
+
+ fail:
+ if (fd > 0)
+ meta_anonymous_file_close_fd (fd);
+ if (other_fd > 0)
+ meta_anonymous_file_close_fd (other_fd);
+ meta_anonymous_file_free (file);
+ return EXIT_FAILURE;
+}