diff --git a/cogl/tests/conform/mutter-cogl.test.in b/cogl/tests/conform/muffin-cogl.test.in similarity index 100% rename from cogl/tests/conform/mutter-cogl.test.in rename to cogl/tests/conform/muffin-cogl.test.in diff --git a/config.h.meson b/config.h.meson index 6fb8c3af8..b28bd2dea 100644 --- a/config.h.meson +++ b/config.h.meson @@ -76,3 +76,12 @@ /* Whether the Xwayland supports +/-byteswappedclients */ #mesondefine HAVE_XWAYLAND_BYTE_SWAPPED_CLIENTS + +/* Whether the mkostemp function exists */ +#mesondefine HAVE_MKOSTEMP + +/* Whether the posix_fallocate function exists */ +#mesondefine HAVE_POSIX_FALLOCATE + +/* Whether the memfd_create function exists */ +#mesondefine HAVE_MEMFD_CREATE diff --git a/debian/libmuffin0.symbols b/debian/libmuffin0.symbols index 999b34fad..019b42a8e 100644 --- a/debian/libmuffin0.symbols +++ b/debian/libmuffin0.symbols @@ -2022,6 +2022,11 @@ libmuffin.so.0 libmuffin0 #MINVER# meta_activate_session@Base 6.0.0 meta_add_clutter_debug_flags@Base 5.3.0 meta_add_verbose_topic@Base 5.3.0 + meta_anonymous_file_close_fd@Base 6.4.1 + meta_anonymous_file_free@Base 6.4.1 + meta_anonymous_file_new@Base 6.4.1 + meta_anonymous_file_open_fd@Base 6.4.1 + meta_anonymous_file_size@Base 6.4.1 meta_backend_add_gpu@Base 5.3.0 meta_backend_get_dnd@Base 5.3.0 meta_backend_get_gpus@Base 5.3.0 diff --git a/meson.build b/meson.build index 79a0e8144..1ef5033c9 100644 --- a/meson.build +++ b/meson.build @@ -375,6 +375,20 @@ if cc.has_header_symbol('sys/prctl.h', 'prctl') cdata.set('HAVE_SYS_PRCTL', 1) endif +optional_functions = [ + 'mkostemp', + 'posix_fallocate', + 'memfd_create', +] + +foreach function : optional_functions + if cc.has_function(function) + cdata.set('HAVE_' + function.to_upper(), 1) + else + message('Optional function ' + function + ' missing') + endif +endforeach + have_xwayland_initfd = false have_xwayland_listenfd = false diff --git a/src/core/meta-anonymous-file.c b/src/core/meta-anonymous-file.c new file mode 100644 index 000000000..d27164a32 --- /dev/null +++ b/src/core/meta-anonymous-file.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2020 Sebastian Wick + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Sebastian Wick + */ + + + +#include "config.h" + +#include +#include +#include + +#include "core/meta-anonymous-file.h" + +struct _MetaAnonymousFile +{ + int fd; + size_t size; +}; + +#define READONLY_SEALS (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) + +static int +create_tmpfile_cloexec (char *tmpname) +{ + int fd; + +#if defined(HAVE_MKOSTEMP) + fd = mkostemp (tmpname, O_CLOEXEC); + if (fd >= 0) + unlink (tmpname); +#else + fd = mkstemp (tmpname); + if (fd >= 0) + { + long flags; + + unlink (tmpname); + + flags = fcntl (fd, F_GETFD); + if (flags == -1 || + fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1) + { + close (fd); + return -1; + } + } +#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. + */ +static int +create_anonymous_file (off_t size) +{ + int fd, ret; + +#if defined(HAVE_MEMFD_CREATE) + fd = memfd_create ("muffin-shared", 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); + } + else +#endif + { + static const char template[] = "/muffin-shared-XXXXXX"; + const char *path; + char *name; + + path = getenv ("XDG_RUNTIME_DIR"); + if (!path) + { + errno = ENOENT; + return -1; + } + + name = g_malloc (strlen (path) + sizeof (template)); + if (!name) + return -1; + + strcpy (name, path); + strcat (name, template); + + fd = create_tmpfile_cloexec (name); + + g_free (name); + + if (fd < 0) + return -1; + } + +#if defined(HAVE_POSIX_FALLOCATE) + do + { + ret = posix_fallocate (fd, 0, size); + } + while (ret == EINTR); + + if (ret != 0) + { + close (fd); + errno = ret; + return -1; + } +#else + do + { + ret = ftruncate (fd, size); + } + while (ret < 0 && errno == EINTR); + + if (ret < 0) + { + close (fd); + return -1; + } +#endif + + return fd; +} + +/** + * meta_anonymous_file_new: (skip) + * @size: The size of @data + * @data: The data of the file with the size @size + * + * Create a new anonymous read-only file of the given size and the given data + * The intended use-case is for sending mid-sized data from the compositor + * to clients. + * + * When done, free the data using meta_anonymous_file_free(). + * + * If this function fails errno is set. + * + * Returns: The newly created #MetaAnonymousFile, or NULL on failure. Use + * meta_anonymous_file_free() to free the resources when done. + */ +MetaAnonymousFile * +meta_anonymous_file_new (size_t size, + const uint8_t *data) +{ + MetaAnonymousFile *file; + void *map; + + file = g_malloc0 (sizeof *file); + if (!file) + { + errno = ENOMEM; + return NULL; + } + + file->size = size; + file->fd = create_anonymous_file (size); + if (file->fd == -1) + goto err_free; + + map = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, file->fd, 0); + if (map == MAP_FAILED) + goto err_close; + + memcpy (map, data, size); + + munmap (map, size); + +#if defined(HAVE_MEMFD_CREATE) + /* try to put seals on the file to make it read-only so that we can + * return the fd later directly when MAPMODE_SHARED is not set. + * meta_anonymous_file_open_fd can handle the fd even if it is not + * sealed read-only and will instead create a new anonymous file on + * each invocation. + */ + fcntl (file->fd, F_ADD_SEALS, READONLY_SEALS); +#endif + + return file; + + err_close: + close (file->fd); + err_free: + g_free (file); + return NULL; +} + + +/** + * meta_anonymous_file_free: (skip) + * @file: the #MetaAnonymousFile + * + * Free the resources used by an anonymous read-only file. + */ +void +meta_anonymous_file_free (MetaAnonymousFile *file) +{ + close (file->fd); + g_free (file); +} + +/** + * meta_anonymous_file_size: (skip) + * @file: the #MetaAnonymousFile + * + * Get the size of an anonymous read-only file. + * + * Returns: The size of the anonymous read-only file. + */ +size_t +meta_anonymous_file_size (MetaAnonymousFile *file) +{ + return file->size; +} + +/** + * meta_anonymous_file_open_fd: (skip) + * @file: the #MetaAnonymousFile to get a file descriptor for + * @mapmode: describes the ways in which the returned file descriptor can + * be used with mmap + * + * Returns a file descriptor for the given file, ready to be sent to a client. + * The returned file descriptor must not be shared between multiple clients. + * If @mapmode is %META_ANONYMOUS_FILE_MAPMODE_PRIVATE the file descriptor is + * only guaranteed to be mmapable with MAP_PRIVATE. If @mapmode is + * %META_ANONYMOUS_FILE_MAPMODE_SHARED the file descriptor can be mmaped with + * either MAP_PRIVATE or MAP_SHARED. + * + * In case %META_ANONYMOUS_FILE_MAPMODE_PRIVATE is used, it is important to + * only read the returned fd using mmap() since using read() will move the + * read cursor of the fd and thus may cause read() calls on other returned + * fds to fail. + * + * When done using the fd, it is required to call meta_anonymous_file_close_fd() + * instead of close(). + * + * If this function fails errno is set. + * + * Returns: A file descriptor for the given file that can be sent to a client + * or -1 on failure. Use meta_anonymous_file_close_fd() to release the fd + * when done. + */ +int +meta_anonymous_file_open_fd (MetaAnonymousFile *file, + MetaAnonymousFileMapmode mapmode) +{ + void *src, *dst; + int fd; + +#if defined(HAVE_MEMFD_CREATE) + int seals; + + seals = fcntl (file->fd, F_GET_SEALS); + + /* file was sealed for read-only and we don't have to support MAP_SHARED + * so we can simply pass the memfd fd + */ + if (seals != -1 && mapmode == META_ANONYMOUS_FILE_MAPMODE_PRIVATE && + (seals & READONLY_SEALS) == READONLY_SEALS) + return file->fd; +#endif + + /* for all other cases we create a new anonymous file that can be mapped + * with MAP_SHARED and copy the contents to it and return that instead + */ + fd = create_anonymous_file (file->size); + if (fd == -1) + return fd; + + src = mmap (NULL, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0); + if (src == MAP_FAILED) + { + close (fd); + return -1; + } + + dst = mmap (NULL, file->size, PROT_WRITE, MAP_SHARED, fd, 0); + if (dst == MAP_FAILED) + { + close (fd); + munmap (src, file->size); + return -1; + } + + memcpy (dst, src, file->size); + munmap (src, file->size); + munmap (dst, file->size); + + return fd; +} + +/** + * meta_anonymous_file_close_fd: (skip) + * @fd: A file descriptor obtained using meta_anonymous_file_open_fd() + * + * Release a file descriptor returned by meta_anonymous_file_open_fd(). + * This function must be called for every file descriptor created with + * meta_anonymous_file_open_fd() to not leak any resources. + * + * If this function fails errno is set. + */ +void +meta_anonymous_file_close_fd (int fd) +{ +#if defined(HAVE_MEMFD_CREATE) + int seals; + + seals = fcntl (fd, F_GET_SEALS); + if (seals == -1 && errno != EINVAL) + { + g_warning ("Reading seals of anonymous file %d failed", fd); + return; + } + + /* The only case in which we do NOT have to close the file is when the file + * was sealed for read-only + */ + if (seals != -1 && (seals & READONLY_SEALS) == READONLY_SEALS) + return; +#endif + + close (fd); +} diff --git a/src/core/meta-anonymous-file.h b/src/core/meta-anonymous-file.h new file mode 100644 index 000000000..d12dd82f7 --- /dev/null +++ b/src/core/meta-anonymous-file.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 Sebastian Wick + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Sebastian Wick + */ + +#ifndef META_ANONYMOUS_FILE_H +#define META_ANONYMOUS_FILE_H + +#include "meta/common.h" +#include "core/util-private.h" + +typedef struct _MetaAnonymousFile MetaAnonymousFile; + +typedef enum _MetaAnonymousFileMapmode +{ + META_ANONYMOUS_FILE_MAPMODE_PRIVATE, + META_ANONYMOUS_FILE_MAPMODE_SHARED, +} MetaAnonymousFileMapmode; + +META_EXPORT_TEST +MetaAnonymousFile * meta_anonymous_file_new (size_t size, + const uint8_t *data); + +META_EXPORT_TEST +void meta_anonymous_file_free (MetaAnonymousFile *file); + +META_EXPORT_TEST +size_t meta_anonymous_file_size (MetaAnonymousFile *file); + +META_EXPORT_TEST +int meta_anonymous_file_open_fd (MetaAnonymousFile *file, + MetaAnonymousFileMapmode mapmode); + +META_EXPORT_TEST +void meta_anonymous_file_close_fd (int fd); + +#endif /* META_ANONYMOUS_FILE_H */ diff --git a/src/meson.build b/src/meson.build index 10fb80dc5..84ded3b72 100644 --- a/src/meson.build +++ b/src/meson.build @@ -345,6 +345,8 @@ muffin_sources = [ 'core/main-private.h', 'core/meta-accel-parse.c', 'core/meta-accel-parse.h', + 'core/meta-anonymous-file.c', + 'core/meta-anonymous-file.h', 'core/meta-border.c', 'core/meta-border.h', 'core/meta-clipboard-manager.c', diff --git a/src/tests/mutter-all.test.in b/src/tests/muffin-all.test.in similarity index 100% rename from src/tests/mutter-all.test.in rename to src/tests/muffin-all.test.in diff --git a/src/tests/wayland-test-clients/meson.build b/src/tests/wayland-test-clients/meson.build index af10f93a9..f78eefcc2 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, + libmuffin_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..cab387fd5 --- /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; +} diff --git a/src/wayland/meta-wayland-keyboard.c b/src/wayland/meta-wayland-keyboard.c index 4c132e8fe..1f050e689 100644 --- a/src/wayland/meta-wayland-keyboard.c +++ b/src/wayland/meta-wayland-keyboard.c @@ -48,15 +48,14 @@ #include "config.h" #include -#include #include #include #include -#include #include #include "backends/meta-backend-private.h" #include "core/display-private.h" +#include "core/meta-anonymous-file.h" #include "wayland/meta-wayland-private.h" #ifdef HAVE_NATIVE_BACKEND @@ -79,86 +78,35 @@ unbind_resource (struct wl_resource *resource) wl_list_remove (wl_resource_get_link (resource)); } -static int -create_anonymous_file (off_t size, - GError **error) -{ - static const char template[] = "muffin-shared-XXXXXX"; - char *path; - int fd, flags; - - fd = g_file_open_tmp (template, &path, error); - - if (fd == -1) - return -1; - - unlink (path); - g_free (path); - - flags = fcntl (fd, F_GETFD); - if (flags == -1) - goto err; - - if (fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1) - goto err; - - if (ftruncate (fd, size) < 0) - goto err; - - return fd; - - err: - g_set_error_literal (error, - G_FILE_ERROR, - g_file_error_from_errno (errno), - strerror (errno)); - close (fd); - - return -1; -} - static void send_keymap (MetaWaylandKeyboard *keyboard, struct wl_resource *resource) { MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info; - GError *error = NULL; int fd; - char *keymap_area; + size_t size; + MetaAnonymousFileMapmode mapmode; - if (!xkb_info->keymap_string) - return; + if (wl_resource_get_version (resource) < 7) + mapmode = META_ANONYMOUS_FILE_MAPMODE_SHARED; + else + mapmode = META_ANONYMOUS_FILE_MAPMODE_PRIVATE; - fd = create_anonymous_file (xkb_info->keymap_size, &error); - if (fd < 0) - { - g_warning ("Creating a keymap file for %lu bytes failed: %s", - (unsigned long) xkb_info->keymap_size, - error->message); - g_clear_error (&error); - return; - } + fd = meta_anonymous_file_open_fd (xkb_info->keymap_rofile, mapmode); + size = meta_anonymous_file_size (xkb_info->keymap_rofile); - keymap_area = mmap (NULL, xkb_info->keymap_size, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (keymap_area == MAP_FAILED) + if (fd == -1) { - g_warning ("Failed to mmap() %lu bytes\n", - (unsigned long) xkb_info->keymap_size); - close (fd); + g_warning ("Creating a keymap file failed: %s", strerror (errno)); return; } - strcpy (keymap_area, xkb_info->keymap_string); - - munmap (keymap_area, xkb_info->keymap_size); - wl_keyboard_send_keymap (resource, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, - fd, - keyboard->xkb_info.keymap_size); - close (fd); + fd, size); + + meta_anonymous_file_close_fd (fd); } static void @@ -177,6 +125,8 @@ meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard, struct xkb_keymap *keymap) { MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info; + char *keymap_string; + size_t keymap_size; if (keymap == NULL) { @@ -184,20 +134,30 @@ meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard, return; } - g_clear_pointer (&xkb_info->keymap_string, g_free); xkb_keymap_unref (xkb_info->keymap); xkb_info->keymap = xkb_keymap_ref (keymap); meta_wayland_keyboard_update_xkb_state (keyboard); - xkb_info->keymap_string = + keymap_string = xkb_keymap_get_as_string (xkb_info->keymap, XKB_KEYMAP_FORMAT_TEXT_V1); - if (!xkb_info->keymap_string) + if (!keymap_string) { g_warning ("Failed to get string version of keymap"); return; } - xkb_info->keymap_size = strlen (xkb_info->keymap_string) + 1; + keymap_size = strlen (keymap_string) + 1; + + xkb_info->keymap_rofile = + meta_anonymous_file_new (keymap_size, (const uint8_t *) keymap_string); + + free (keymap_string); + + if (!xkb_info->keymap_rofile) + { + g_warning ("Failed to create anonymous file for keymap"); + return; + } inform_clients_of_new_keymap (keyboard); @@ -590,7 +550,7 @@ meta_wayland_xkb_info_destroy (MetaWaylandXkbInfo *xkb_info) { g_clear_pointer (&xkb_info->keymap, xkb_keymap_unref); g_clear_pointer (&xkb_info->state, xkb_state_unref); - g_clear_pointer (&xkb_info->keymap_string, g_free); + meta_anonymous_file_free (xkb_info->keymap_rofile); } void diff --git a/src/wayland/meta-wayland-keyboard.h b/src/wayland/meta-wayland-keyboard.h index d708b03f8..6e8a61674 100644 --- a/src/wayland/meta-wayland-keyboard.h +++ b/src/wayland/meta-wayland-keyboard.h @@ -49,6 +49,7 @@ #include #include "clutter/clutter.h" +#include "core/meta-anonymous-file.h" #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_KEYBOARD (meta_wayland_keyboard_get_type ()) @@ -74,8 +75,7 @@ typedef struct { struct xkb_keymap *keymap; struct xkb_state *state; - size_t keymap_size; - char *keymap_string; + MetaAnonymousFile *keymap_rofile; } MetaWaylandXkbInfo; struct _MetaWaylandKeyboard