Signed-off-by: Leonid Bobrov <mazoc...@disroot.org>
---
 configure.ac         |  3 ++
 src/wayland-server.c | 46 ++++++++++++++++++++++
 src/wayland-shm.c    | 92 +++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 140 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 912330e..d106a41 100644
--- a/configure.ac
+++ b/configure.ac
@@ -65,6 +65,9 @@ AC_SUBST(GCC_CFLAGS)
 AC_CHECK_HEADERS([sys/prctl.h])
 AC_CHECK_FUNCS([accept4 mkostemp posix_fallocate prctl])
 
+# Credential support on BSD
+AC_CHECK_HEADERS([sys/ucred.h])
+
 # Replacement for /proc on BSD
 AC_CHECK_HEADERS([kvm.h])
 SAVE_LIBS="$LIBS"
diff --git a/src/wayland-server.c b/src/wayland-server.c
index 19f6a76..f4cdbf3 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -25,6 +25,13 @@
 
 #define _GNU_SOURCE
 
+#include "../config.h"
+
+#ifdef HAVE_SYS_UCRED_H
+#include <sys/types.h>
+#include <sys/ucred.h>
+#endif
+
 #include <stdlib.h>
 #include <stdint.h>
 #include <stddef.h>
@@ -77,7 +84,13 @@ struct wl_client {
        struct wl_list link;
        struct wl_map objects;
        struct wl_priv_signal destroy_signal;
+#ifdef HAVE_SYS_UCRED_H
+       /* BSD */
+       struct xucred xucred;
+#else
+       /* Linux */
        struct ucred ucred;
+#endif
        int error;
        struct wl_priv_signal resource_created_signal;
 };
@@ -312,7 +325,11 @@ wl_resource_post_error(struct wl_resource *resource,
 static void
 destroy_client_with_error(struct wl_client *client, const char *reason)
 {
+#ifdef HAVE_SYS_UCRED_H
+       wl_log("%s (uid %u)\n", reason, client->xucred.cr_uid);
+#else
        wl_log("%s (pid %u)\n", reason, client->ucred.pid);
+#endif
        wl_client_destroy(client);
 }
 
@@ -526,10 +543,29 @@ wl_client_create(struct wl_display *display, int fd)
        if (!client->source)
                goto err_client;
 
+#ifndef SO_PEERCRED
+/* FreeBSD */
+# define SO_PEERCRED LOCAL_PEERCRED
+#endif
+
+#ifdef HAVE_SYS_UCRED_H
+       /* BSD */
+       len = sizeof client->xucred;
+       if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED,
+                      &client->xucred, &len) < 0
+# ifdef XUCRED_VERSION
+                      /* FreeBSD */
+                      || client->xucred.cr_version != XUCRED_VERSION
+# endif
+                     )
+               goto err_source;
+#else
+       /* Linux */
        len = sizeof client->ucred;
        if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED,
                       &client->ucred, &len) < 0)
                goto err_source;
+#endif
 
        client->connection = wl_connection_create(fd);
        if (client->connection == NULL)
@@ -583,12 +619,22 @@ WL_EXPORT void
 wl_client_get_credentials(struct wl_client *client,
                          pid_t *pid, uid_t *uid, gid_t *gid)
 {
+#ifdef HAVE_SYS_UCRED_H
+       /* BSD */
+       *pid = 0; /* FIXME: pid is not defined on BSD */
+       if (uid)
+               *uid = client->xucred.cr_uid;
+       if (gid)
+               *gid = client->xucred.cr_gid;
+#else
+       /* Linux */
        if (pid)
                *pid = client->ucred.pid;
        if (uid)
                *uid = client->ucred.uid;
        if (gid)
                *gid = client->ucred.gid;
+#endif
 }
 
 /** Get the file descriptor for the client
diff --git a/src/wayland-shm.c b/src/wayland-shm.c
index 4191231..dbaf464 100644
--- a/src/wayland-shm.c
+++ b/src/wayland-shm.c
@@ -30,6 +30,8 @@
 
 #define _GNU_SOURCE
 
+#include "../config.h"
+
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -59,6 +61,9 @@ struct wl_shm_pool {
        char *data;
        int32_t size;
        int32_t new_size;
+#ifdef HAVE_SYS_UCRED_H
+       int fd;
+#endif
 };
 
 struct wl_shm_buffer {
@@ -76,15 +81,91 @@ struct wl_shm_sigbus_data {
        int fallback_mapping_used;
 };
 
+#ifdef HAVE_MREMAP
+static void *
+mremap_compat_maymove(void *old_address, size_t old_size, size_t new_size,
+                     int old_prot, int old_flags, int old_fd)
+{
+       return mremap(old_address, old_size, new_size, MREMAP_MAYMOVE);
+}
+#else
+static void *
+mremap_compat_maymove(void *old_address, size_t old_size, size_t new_size,
+                     int old_prot, int old_flags, int old_fd)
+{
+       /* FreeBSD doesn't support mremap() yet, so we have to emulate it.
+        * This assumes MREMAP_MAYMOVE is the only flag in use. */
+       if (new_size == old_size) {
+               return old_address;
+       } else if (new_size < old_size) {
+               /* Shrinking: munmap() the spare region. */
+               munmap(old_address + old_size, new_size - old_size);
+               return old_address;
+       } else {
+               void *ret;
+
+               /* Growing. Try and mmap() the extra region at the end of
+                * our existing allocation. If that gets mapped in the
+                * wrong place, fall back to mmap()ing an entirely new
+                * region of new_size and copying the data across. */
+               ret = mmap(old_address + old_size, new_size - old_size,
+                          old_prot, old_flags, old_fd, 0);
+
+/* FIXME TODO: msync() before munmap()? */
+               if (ret == MAP_FAILED) {
+                       /* Total failure! */
+                       return ret;
+               } else if (ret == old_address + old_size) {
+                       /* Success. */
+                       return old_address;
+               } else if (ret != old_address + old_size) {
+                       /* Partial failure. Fall back to mapping an
+                        * entirely new region. Unmap the region we
+                        * just mapped first. */
+                       munmap(ret, new_size - old_size);
+
+                       /* Map an entirely new region. */
+                       ret = mmap(NULL, new_size,
+                                  old_prot, old_flags, old_fd, 0);
+                       if (ret == MAP_FAILED) {
+                               /* Total failure! */
+                               return ret;
+                       }
+
+                       /* Copy the old data across. Implicit assumption
+                        * that the old and new regions don't overlap. */
+                       memcpy(ret, old_address, old_size);
+
+                       /* Unmap the old region. */
+                       munmap(old_address, old_size);
+
+                       return ret;
+               }
+       }
+
+       /* Unreachable. */
+       return MAP_FAILED;
+}
+#endif
+
 static void
 shm_pool_finish_resize(struct wl_shm_pool *pool)
 {
        void *data;
+#ifdef HAVE_SYS_UCRED_H
+       int fd = -1;
+#endif
 
        if (pool->size == pool->new_size)
                return;
 
+#ifdef HAVE_SYS_UCRED_H
+       fd = pool->fd;
+       data = mremap_compat_maymove(pool->data, pool->size, pool->new_size,
+                                    PROT_READ|PROT_WRITE, MAP_SHARED, fd);
+#else
        data = mremap(pool->data, pool->size, pool->new_size, MREMAP_MAYMOVE);
+#endif
        if (data == MAP_FAILED) {
                wl_resource_post_error(pool->resource,
                                       WL_SHM_ERROR_INVALID_FD,
@@ -110,6 +191,10 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external)
        if (pool->internal_refcount + pool->external_refcount)
                return;
 
+#ifdef HAVE_SYS_UCRED_H
+       close(pool->fd);
+#endif
+
        munmap(pool->data, pool->size);
        free(pool);
 }
@@ -284,7 +369,13 @@ shm_create_pool(struct wl_client *client, struct 
wl_resource *resource,
                                       "failed mmap fd %d: %m", fd);
                goto err_free;
        }
+#ifdef HAVE_SYS_UCRED_H
+       /* We need to keep the FD around on FreeBSD so we can implement
+        * mremap(). See: mremap_compat_maymove(). */
+       pool->fd = fd;
+#else
        close(fd);
+#endif
 
        pool->resource =
                wl_resource_create(client, &wl_shm_pool_interface, 1, id);
@@ -364,7 +455,6 @@ wl_shm_buffer_get_stride(struct wl_shm_buffer *buffer)
        return buffer->stride;
 }
 
-
 /** Get a pointer to the memory for the SHM buffer
  *
  * \param buffer The buffer object
-- 
2.20.1

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to