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