From 4b8b2865e1e59a30f4e43945656bae1e7a350424 Mon Sep 17 00:00:00 2001
From: Jan Beich <jbeich@FreeBSD.org>
Date: Sat, 7 Aug 2021 05:53:50 +0000
Subject: [PATCH] build: add memfd_create fallback from libwayland

memfd_create only exists on Linux and FreeBSD >= 13. On other systems
use a temporary file under XDG_RUNTIME_DIR but resize with ftruncate()
in case posix_fallocate() fails on ZFS.

meson.build:56:4: ERROR: Problem encountered: Required function memfd_create missing

src/plugins/cairo/libdecor-cairo.c:547:7: warning: implicit declaration of function 'memfd_create' is invalid in C99 [-Wimplicit-function-declaration]
        fd = memfd_create("libdecor-cairo", MFD_CLOEXEC | MFD_ALLOW_SEALING);
             ^
src/plugins/cairo/libdecor-cairo.c:547:38: error: use of undeclared identifier 'MFD_CLOEXEC'
        fd = memfd_create("libdecor-cairo", MFD_CLOEXEC | MFD_ALLOW_SEALING);
                                            ^
src/plugins/cairo/libdecor-cairo.c:547:52: error: use of undeclared identifier 'MFD_ALLOW_SEALING'
        fd = memfd_create("libdecor-cairo", MFD_CLOEXEC | MFD_ALLOW_SEALING);
                                                          ^
src/plugins/cairo/libdecor-cairo.c:552:12: error: use of undeclared identifier 'F_ADD_SEALS'
        fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
                  ^
src/plugins/cairo/libdecor-cairo.c:552:25: error: use of undeclared identifier 'F_SEAL_SHRINK'
        fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
                               ^
---
 config.h.meson                     |   4 +
 demo/c++-demo.cc                   |  29 +----
 demo/demo.c                        |  29 +----
 demo/meson.build                   |   2 +
 meson.build                        |  12 +-
 src/meson.build                    |  10 ++
 src/os-compatibility.c             | 183 +++++++++++++++++++++++++++++
 src/os-compatibility.h             |  37 ++++++
 src/plugins/cairo/libdecor-cairo.c |  29 +----
 src/plugins/cairo/meson.build      |   1 +
 10 files changed, 255 insertions(+), 81 deletions(-)
 create mode 100644 src/os-compatibility.c
 create mode 100644 src/os-compatibility.h

diff --git a/config.h.meson b/config.h.meson
index 5a7c322..ba18577 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -8,3 +8,7 @@
 #mesondefine LIBDECOR_PLUGIN_API_VERSION
 
 #mesondefine HAS_DBUS
+
+#mesondefine HAVE_MKOSTEMP
+#mesondefine HAVE_POSIX_FALLOCATE
+#mesondefine HAVE_MEMFD_CREATE
diff --git a/demo/c++-demo.cc b/demo/c++-demo.cc
index bc46354..2c99022 100644
--- a/demo/c++-demo.cc
+++ b/demo/c++-demo.cc
@@ -42,6 +42,9 @@
 #include "libdecor.h"
 #include "utils.h"
 #include "cursor-settings.h"
+extern "C" {
+#include "os-compatibility.h"
+}
 
 #include "xdg-shell-client-protocol.h"
 
@@ -91,7 +94,7 @@ public:
 		stride = width * 4;
 		size = stride * height;
 
-		fd = create_anonymous_file(size);
+		fd = os_create_anonymous_file(size);
 		if (fd < 0) {
 			cerr << "Creating a buffer file for " << size <<
 				" B failed: " << strerror(errno) << endl;
@@ -144,30 +147,6 @@ public:
 	}
 
 private:
-	static int create_anonymous_file(off_t size)
-	{
-		int fd;
-		int ret;
-
-		fd = memfd_create("libdecor-demo", MFD_CLOEXEC | MFD_ALLOW_SEALING);
-
-		if (fd < 0)
-			return -1;
-
-		fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
-
-		do {
-			ret = posix_fallocate(fd, 0, size);
-		} while (ret == EINTR);
-		if (ret != 0) {
-			close(fd);
-			errno = ret;
-			return -1;
-		}
-
-		return fd;
-	}
-
 	static void buffer_release(void *user_data,
 				   struct wl_buffer *wl_buffer)
 	{
diff --git a/demo/demo.c b/demo/demo.c
index b6c48b0..351f57b 100644
--- a/demo/demo.c
+++ b/demo/demo.c
@@ -41,6 +41,7 @@
 #include "libdecor.h"
 #include "utils.h"
 #include "cursor-settings.h"
+#include "os-compatibility.h"
 
 #include "xdg-shell-client-protocol.h"
 
@@ -986,32 +987,6 @@ static const struct wl_buffer_listener buffer_listener = {
 	buffer_release
 };
 
-static int
-create_anonymous_file(off_t size)
-{
-	int fd;
-
-	int ret;
-
-	fd = memfd_create("libdecor-demo", MFD_CLOEXEC | MFD_ALLOW_SEALING);
-
-	if (fd < 0)
-		return -1;
-
-	fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
-
-	do {
-		ret = posix_fallocate(fd, 0, size);
-	} while (ret == EINTR);
-	if (ret != 0) {
-		close(fd);
-		errno = ret;
-		return -1;
-	}
-
-	return fd;
-}
-
 static struct buffer *
 create_shm_buffer(int width,
 		  int height,
@@ -1025,7 +1000,7 @@ create_shm_buffer(int width,
 	stride = width * 4;
 	size = stride * height;
 
-	fd = create_anonymous_file(size);
+	fd = os_create_anonymous_file(size);
 	if (fd < 0) {
 		fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
 			size, strerror(errno));
diff --git a/demo/meson.build b/demo/meson.build
index a65125b..3370bdc 100644
--- a/demo/meson.build
+++ b/demo/meson.build
@@ -46,6 +46,7 @@ executable('libdecor-demo',
     libdecor_dep,
     wayland_cursor_dep,
     cursor_settings_dep,
+    os_compatibility_dep,
     xkb_dep],
   install: get_option('install_demo'),
 )
@@ -78,6 +79,7 @@ executable('libdecor-c++-demo',
     libdecor_dep,
     wayland_cursor_dep,
     cursor_settings_dep,
+    os_compatibility_dep,
     xkb_dep],
   install: get_option('install_demo'),
 )
diff --git a/meson.build b/meson.build
index 94c7308..99dc13e 100644
--- a/meson.build
+++ b/meson.build
@@ -47,8 +47,6 @@ dbus_dep = dependency('dbus-1', version: dbus_req, required: get_option('dbus'))
 
 # list of (function, prefix)
 required_functions = [
-  ['memfd_create', '#define _GNU_SOURCE\n#include <sys/mman.h>'],
-  ['posix_fallocate', '#include <fcntl.h>'],
   ['asprintf', '#define _GNU_SOURCE\n#include <stdio.h>'],
 ]
 foreach function : required_functions
@@ -77,6 +75,16 @@ if dbus_dep.found()
   cdata.set('HAS_DBUS', true)
 endif
 
+if cc.has_function('mkostemp', prefix: '#include <stdlib.h>')
+  cdata.set('HAVE_MKOSTEMP', true)
+endif
+if cc.has_function('posix_fallocate', prefix: '#include <fcntl.h>')
+  cdata.set('HAVE_POSIX_FALLOCATE', true)
+endif
+if cc.has_function('memfd_create', prefix: '#define _GNU_SOURCE\n#include <sys/mman.h>')
+  cdata.set('HAVE_MEMFD_CREATE', true)
+endif
+
 config_h = configure_file(
   input: 'config.h.meson',
   output: 'config.h',
diff --git a/src/meson.build b/src/meson.build
index 52b8f54..f1e434e 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -79,6 +79,16 @@ cursor_settings_dep = declare_dependency(
   dependencies: [dbus_dep],
 )
 
+## os compatibility
+os_compatibility = static_library('os_compatibility',
+  sources: ['os-compatibility.c'],
+  include_directories: [top_includepath],
+)
+
+os_compatibility_dep = declare_dependency(
+  link_with: os_compatibility,
+)
+
 ## core decor library
 libdecor = shared_library(libdecor_name,
   sources: [
diff --git a/src/os-compatibility.c b/src/os-compatibility.c
new file mode 100644
index 0000000..8d51e52
--- /dev/null
+++ b/src/os-compatibility.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef HAVE_MEMFD_CREATE
+#include <sys/mman.h>
+#endif
+
+#include "os-compatibility.h"
+
+#ifndef HAVE_MKOSTEMP
+static int
+set_cloexec_or_close(int fd)
+{
+	long flags;
+
+	if (fd == -1)
+		return -1;
+
+	flags = fcntl(fd, F_GETFD);
+	if (flags == -1)
+		goto err;
+
+	if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+		goto err;
+
+	return fd;
+
+err:
+	close(fd);
+	return -1;
+}
+#endif
+
+static int
+create_tmpfile_cloexec(char *tmpname)
+{
+	int fd;
+
+#ifdef HAVE_MKOSTEMP
+	fd = mkostemp(tmpname, O_CLOEXEC);
+	if (fd >= 0)
+		unlink(tmpname);
+#else
+	fd = mkstemp(tmpname);
+	if (fd >= 0) {
+		fd = set_cloexec_or_close(fd);
+		unlink(tmpname);
+	}
+#endif
+
+	return fd;
+}
+
+/*
+ * Create a new, unique, anonymous file of the given size, and
+ * return the file descriptor for it. The file descriptor is set
+ * CLOEXEC. The file is immediately suitable for mmap()'ing
+ * the given size at offset zero.
+ *
+ * The file should not have a permanent backing store like a disk,
+ * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
+ *
+ * The file name is deleted from the file system.
+ *
+ * The file is suitable for buffer sharing between processes by
+ * transmitting the file descriptor over Unix sockets using the
+ * SCM_RIGHTS methods.
+ *
+ * If the C library implements posix_fallocate(), it is used to
+ * guarantee that disk space is available for the file at the
+ * given size. If disk space is insufficient, errno is set to ENOSPC.
+ * If posix_fallocate() is not supported, program may receive
+ * SIGBUS on accessing mmap()'ed file contents instead.
+ *
+ * If the C library implements memfd_create(), it is used to create the
+ * file purely in memory, without any backing file name on the file
+ * system, and then sealing off the possibility of shrinking it.  This
+ * can then be checked before accessing mmap()'ed file contents, to
+ * make sure SIGBUS can't happen.  It also avoids requiring
+ * XDG_RUNTIME_DIR.
+ */
+int
+os_create_anonymous_file(off_t size)
+{
+	static const char template[] = "/wayland-cursor-shared-XXXXXX";
+	const char *path;
+	char *name;
+	int fd;
+
+#ifdef HAVE_MEMFD_CREATE
+	fd = memfd_create("wayland-cursor", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+	if (fd >= 0) {
+		/* We can add this seal before calling posix_fallocate(), as
+		 * the file is currently zero-sized anyway.
+		 *
+		 * There is also no need to check for the return value, we
+		 * couldn't do anything with it anyway.
+		 */
+		fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
+	} else
+#endif
+	{
+		path = getenv("XDG_RUNTIME_DIR");
+		if (!path) {
+			errno = ENOENT;
+			return -1;
+		}
+
+		name = malloc(strlen(path) + sizeof(template));
+		if (!name)
+			return -1;
+
+		strcpy(name, path);
+		strcat(name, template);
+
+		fd = create_tmpfile_cloexec(name);
+
+		free(name);
+
+		if (fd < 0)
+			return -1;
+	}
+
+	if (os_resize_anonymous_file(fd, size) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+int
+os_resize_anonymous_file(int fd, off_t size)
+{
+#ifdef HAVE_POSIX_FALLOCATE
+	/* 
+	 * Filesystems that do support fallocate will return EINVAL or
+	 * EOPNOTSUPP. In this case we need to fall back to ftruncate
+	 */
+	errno = posix_fallocate(fd, 0, size);
+	if (errno == 0)
+		return 0;
+	else if (errno != EINVAL && errno != EOPNOTSUPP)
+		return -1;
+#endif
+	if (ftruncate(fd, size) < 0)
+		return -1;
+
+	return 0;
+}
diff --git a/src/os-compatibility.h b/src/os-compatibility.h
new file mode 100644
index 0000000..fdfeb78
--- /dev/null
+++ b/src/os-compatibility.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef OS_COMPATIBILITY_H
+#define OS_COMPATIBILITY_H
+
+#include <sys/types.h>
+
+int
+os_create_anonymous_file(off_t size);
+
+int
+os_resize_anonymous_file(int fd, off_t size);
+
+#endif /* OS_COMPATIBILITY_H */
diff --git a/src/plugins/cairo/libdecor-cairo.c b/src/plugins/cairo/libdecor-cairo.c
index cfc3c83..96d7047 100644
--- a/src/plugins/cairo/libdecor-cairo.c
+++ b/src/plugins/cairo/libdecor-cairo.c
@@ -39,6 +39,7 @@
 #include "libdecor-plugin.h"
 #include "utils.h"
 #include "cursor-settings.h"
+#include "os-compatibility.h"
 
 #include <cairo/cairo.h>
 #include <pango/pangocairo.h>
@@ -537,32 +538,6 @@ libdecor_plugin_cairo_frame_new(struct libdecor_plugin *plugin)
 	return &frame_cairo->frame;
 }
 
-static int
-create_anonymous_file(off_t size)
-{
-	int ret;
-
-	int fd;
-
-	fd = memfd_create("libdecor-cairo", MFD_CLOEXEC | MFD_ALLOW_SEALING);
-
-	if (fd < 0)
-		return -1;
-
-	fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
-
-	do {
-		ret = posix_fallocate(fd, 0, size);
-	} while (ret == EINTR);
-	if (ret != 0) {
-		close(fd);
-		errno = ret;
-		return -1;
-	}
-
-	return fd;
-}
-
 static void
 toggle_maximized(struct libdecor_frame *const frame)
 {
@@ -610,7 +585,7 @@ create_shm_buffer(struct libdecor_plugin_cairo *plugin_cairo,
 	stride = buffer_width * 4;
 	size = stride * buffer_height;
 
-	fd = create_anonymous_file(size);
+	fd = os_create_anonymous_file(size);
 	if (fd < 0) {
 		fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
 			size, strerror(errno));
diff --git a/src/plugins/cairo/meson.build b/src/plugins/cairo/meson.build
index 8615f4d..bf0a6ee 100644
--- a/src/plugins/cairo/meson.build
+++ b/src/plugins/cairo/meson.build
@@ -20,6 +20,7 @@ shared_module('decor-cairo',
     math_dep,
     wayland_cursor_dep,
     cursor_settings_dep,
+    os_compatibility_dep,
   ],
   install_dir: join_paths(plugindir),
   install: true,
-- 
GitLab

