1 /*
2  * Copyright © 2014 Intel Corporation
3  * Copyright © 2012 Collabora, Ltd.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software
6  * and its documentation for any purpose is hereby granted without
7  * fee, provided that the above copyright notice appear in all copies
8  * and that both that copyright notice and this permission notice
9  * appear in supporting documentation, and that the name of the
10  * copyright holders not be used in advertising or publicity
11  * pertaining to distribution of the software without specific,
12  * written prior permission.  The copyright holders make no
13  * representations about the suitability of this software for any
14  * purpose.  It is provided "as is" without express or implied
15  * warranty.
16  *
17  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
18  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
19  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
20  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
22  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
23  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
24  * SOFTWARE.
25  */
26 
27 #include <xwayland-config.h>
28 
29 #include "os.h"
30 
31 #include <sys/mman.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <stdlib.h>
38 
39 #include "fb.h"
40 #include "pixmapstr.h"
41 
42 #include "xwayland-pixmap.h"
43 #include "xwayland-screen.h"
44 #include "xwayland-shm.h"
45 
46 struct xwl_pixmap {
47     struct wl_buffer *buffer;
48     void *data;
49     size_t size;
50 };
51 
52 #ifndef HAVE_MKOSTEMP
53 static int
set_cloexec_or_close(int fd)54 set_cloexec_or_close(int fd)
55 {
56     long flags;
57 
58     if (fd == -1)
59         return -1;
60 
61     flags = fcntl(fd, F_GETFD);
62     if (flags == -1)
63         goto err;
64 
65     if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
66         goto err;
67 
68     return fd;
69 
70  err:
71     close(fd);
72     return -1;
73 }
74 #endif
75 
76 static int
create_tmpfile_cloexec(char * tmpname)77 create_tmpfile_cloexec(char *tmpname)
78 {
79     int fd;
80 
81 #ifdef HAVE_MKOSTEMP
82     fd = mkostemp(tmpname, O_CLOEXEC);
83     if (fd >= 0)
84         unlink(tmpname);
85 #else
86     fd = mkstemp(tmpname);
87     if (fd >= 0) {
88         fd = set_cloexec_or_close(fd);
89         unlink(tmpname);
90     }
91 #endif
92 
93     return os_move_fd(fd);
94 }
95 
96 /*
97  * Create a new, unique, anonymous file of the given size, and
98  * return the file descriptor for it. The file descriptor is set
99  * CLOEXEC. The file is immediately suitable for mmap()'ing
100  * the given size at offset zero.
101  *
102  * The file should not have a permanent backing store like a disk,
103  * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
104  *
105  * The file name is deleted from the file system.
106  *
107  * The file is suitable for buffer sharing between processes by
108  * transmitting the file descriptor over Unix sockets using the
109  * SCM_RIGHTS methods.
110  *
111  * If the C library implements posix_fallocate(), it is used to
112  * guarantee that disk space is available for the file at the
113  * given size. If disk space is insufficient, errno is set to ENOSPC.
114  * If posix_fallocate() is not supported, program may receive
115  * SIGBUS on accessing mmap()'ed file contents instead.
116  *
117  * If the C library implements memfd_create(), it is used to create the
118  * file purely in memory, without any backing file name on the file
119  * system, and then sealing off the possibility of shrinking it.  This
120  * can then be checked before accessing mmap()'ed file contents, to
121  * make sure SIGBUS can't happen.  It also avoids requiring
122  * XDG_RUNTIME_DIR.
123  */
124 static int
os_create_anonymous_file(off_t size)125 os_create_anonymous_file(off_t size)
126 {
127     static const char template[] = "/xwayland-shared-XXXXXX";
128     const char *path;
129     char *name;
130     int fd;
131     int ret;
132 
133 #ifdef HAVE_MEMFD_CREATE
134     fd = memfd_create("xwayland-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
135     if (fd >= 0) {
136         /* We can add this seal before calling posix_fallocate(), as
137          * the file is currently zero-sized anyway.
138          *
139          * There is also no need to check for the return value, we
140          * couldn't do anything with it anyway.
141          */
142         fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
143     } else
144 #endif
145     {
146         path = getenv("XDG_RUNTIME_DIR");
147         if (!path) {
148             errno = ENOENT;
149             return -1;
150         }
151 
152         name = malloc(strlen(path) + sizeof(template));
153         if (!name)
154             return -1;
155 
156         strcpy(name, path);
157         strcat(name, template);
158 
159         fd = create_tmpfile_cloexec(name);
160 
161         free(name);
162 
163         if (fd < 0)
164             return -1;
165     }
166 
167 #ifdef HAVE_POSIX_FALLOCATE
168     /*
169      * posix_fallocate does an explicit rollback if it gets EINTR.
170      * Temporarily block signals to allow the call to succeed on
171      * slow systems where the smart scheduler's SIGALRM prevents
172      * large allocation attempts from ever succeeding.
173      */
174     OsBlockSignals();
175     do {
176         ret = posix_fallocate(fd, 0, size);
177     } while (ret == EINTR);
178     OsReleaseSignals();
179 
180     if (ret != 0) {
181         close(fd);
182         errno = ret;
183         return -1;
184     }
185 #else
186     do {
187         ret = ftruncate(fd, size);
188     } while (ret == -1 && errno == EINTR);
189 
190     if (ret < 0) {
191         close(fd);
192         return -1;
193     }
194 #endif
195 
196     return fd;
197 }
198 
199 static uint32_t
shm_format_for_depth(int depth)200 shm_format_for_depth(int depth)
201 {
202     switch (depth) {
203     case 32:
204         return WL_SHM_FORMAT_ARGB8888;
205     case 24:
206     default:
207         return WL_SHM_FORMAT_XRGB8888;
208 #ifdef WL_SHM_FORMAT_RGB565
209     case 16:
210         /* XXX: Check run-time protocol version too */
211         return WL_SHM_FORMAT_RGB565;
212 #endif
213     }
214 }
215 
216 static const struct wl_buffer_listener xwl_shm_buffer_listener = {
217     xwl_pixmap_buffer_release_cb,
218 };
219 
220 PixmapPtr
xwl_shm_create_pixmap(ScreenPtr screen,int width,int height,int depth,unsigned int hint)221 xwl_shm_create_pixmap(ScreenPtr screen,
222                       int width, int height, int depth, unsigned int hint)
223 {
224     struct xwl_screen *xwl_screen = xwl_screen_get(screen);
225     struct xwl_pixmap *xwl_pixmap;
226     struct wl_shm_pool *pool;
227     PixmapPtr pixmap;
228     size_t size, stride;
229     uint32_t format;
230     int fd;
231 
232     if (hint == CREATE_PIXMAP_USAGE_GLYPH_PICTURE ||
233         (!xwl_screen->rootless && hint != CREATE_PIXMAP_USAGE_BACKING_PIXMAP) ||
234         (width == 0 && height == 0) || depth < 15)
235         return fbCreatePixmap(screen, width, height, depth, hint);
236 
237     stride = PixmapBytePad(width, depth);
238     size = stride * height;
239     /* Size in the protocol is an integer, make sure we don't exceed
240      * INT32_MAX or else the Wayland compositor will raise an error and
241      * kill the Wayland connection!
242      */
243     if (size > INT32_MAX)
244         return NULL;
245 
246     pixmap = fbCreatePixmap(screen, 0, 0, depth, hint);
247     if (!pixmap)
248         return NULL;
249 
250     xwl_pixmap = calloc(1, sizeof(*xwl_pixmap));
251     if (xwl_pixmap == NULL)
252         goto err_destroy_pixmap;
253 
254     xwl_pixmap->buffer = NULL;
255     xwl_pixmap->size = size;
256     fd = os_create_anonymous_file(size);
257     if (fd < 0)
258         goto err_free_xwl_pixmap;
259 
260     xwl_pixmap->data = mmap(NULL, size, PROT_READ | PROT_WRITE,
261                                   MAP_SHARED, fd, 0);
262     if (xwl_pixmap->data == MAP_FAILED)
263         goto err_close_fd;
264 
265     if (!(*screen->ModifyPixmapHeader) (pixmap, width, height, depth,
266                                         BitsPerPixel(depth),
267                                         stride, xwl_pixmap->data))
268         goto err_munmap;
269 
270     format = shm_format_for_depth(pixmap->drawable.depth);
271     pool = wl_shm_create_pool(xwl_screen->shm, fd, xwl_pixmap->size);
272     xwl_pixmap->buffer = wl_shm_pool_create_buffer(pool, 0,
273                                                    pixmap->drawable.width,
274                                                    pixmap->drawable.height,
275                                                    pixmap->devKind, format);
276     wl_shm_pool_destroy(pool);
277     close(fd);
278 
279     wl_buffer_add_listener(xwl_pixmap->buffer,
280                            &xwl_shm_buffer_listener, pixmap);
281 
282     xwl_pixmap_set_private(pixmap, xwl_pixmap);
283 
284     return pixmap;
285 
286  err_munmap:
287     munmap(xwl_pixmap->data, size);
288  err_close_fd:
289     close(fd);
290  err_free_xwl_pixmap:
291     free(xwl_pixmap);
292  err_destroy_pixmap:
293     fbDestroyPixmap(pixmap);
294 
295     return NULL;
296 }
297 
298 Bool
xwl_shm_destroy_pixmap(PixmapPtr pixmap)299 xwl_shm_destroy_pixmap(PixmapPtr pixmap)
300 {
301     struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
302 
303     if (xwl_pixmap && pixmap->refcnt == 1) {
304         xwl_pixmap_del_buffer_release_cb(pixmap);
305         if (xwl_pixmap->buffer)
306             wl_buffer_destroy(xwl_pixmap->buffer);
307         munmap(xwl_pixmap->data, xwl_pixmap->size);
308         free(xwl_pixmap);
309     }
310 
311     return fbDestroyPixmap(pixmap);
312 }
313 
314 struct wl_buffer *
xwl_shm_pixmap_get_wl_buffer(PixmapPtr pixmap)315 xwl_shm_pixmap_get_wl_buffer(PixmapPtr pixmap)
316 {
317     return xwl_pixmap_get(pixmap)->buffer;
318 }
319 
320 Bool
xwl_shm_create_screen_resources(ScreenPtr screen)321 xwl_shm_create_screen_resources(ScreenPtr screen)
322 {
323     struct xwl_screen *xwl_screen = xwl_screen_get(screen);
324     int ret;
325 
326     screen->CreateScreenResources = xwl_screen->CreateScreenResources;
327     ret = (*screen->CreateScreenResources) (screen);
328     xwl_screen->CreateScreenResources = screen->CreateScreenResources;
329     screen->CreateScreenResources = xwl_shm_create_screen_resources;
330 
331     if (!ret)
332         return ret;
333 
334     if (xwl_screen->rootless)
335         screen->devPrivate =
336             fbCreatePixmap(screen, 0, 0, screen->rootDepth, 0);
337     else
338         screen->devPrivate =
339             xwl_shm_create_pixmap(screen, screen->width, screen->height,
340                                   screen->rootDepth,
341                                   CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
342 
343     SetRootClip(screen, xwl_screen->root_clip_mode);
344 
345     return screen->devPrivate != NULL;
346 }
347