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 #ifdef HAVE_DIX_CONFIG_H
28 #include <dix-config.h>
29 #endif
30 
31 #include "os.h"
32 
33 #include "xwayland.h"
34 
35 #include <sys/mman.h>
36 #include <sys/types.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <string.h>
41 #include <stdlib.h>
42 
43 struct xwl_pixmap {
44     struct wl_buffer *buffer;
45     void *data;
46     size_t size;
47 };
48 
49 #ifndef HAVE_MKOSTEMP
50 static int
set_cloexec_or_close(int fd)51 set_cloexec_or_close(int fd)
52 {
53     long flags;
54 
55     if (fd == -1)
56         return -1;
57 
58     flags = fcntl(fd, F_GETFD);
59     if (flags == -1)
60         goto err;
61 
62     if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
63         goto err;
64 
65     return fd;
66 
67  err:
68     close(fd);
69     return -1;
70 }
71 #endif
72 
73 static int
create_tmpfile_cloexec(char * tmpname)74 create_tmpfile_cloexec(char *tmpname)
75 {
76     int fd;
77 
78 #ifdef HAVE_MKOSTEMP
79     fd = mkostemp(tmpname, O_CLOEXEC);
80     if (fd >= 0)
81         unlink(tmpname);
82 #else
83     fd = mkstemp(tmpname);
84     if (fd >= 0) {
85         fd = set_cloexec_or_close(fd);
86         unlink(tmpname);
87     }
88 #endif
89 
90     return os_move_fd(fd);
91 }
92 
93 /*
94  * Create a new, unique, anonymous file of the given size, and
95  * return the file descriptor for it. The file descriptor is set
96  * CLOEXEC. The file is immediately suitable for mmap()'ing
97  * the given size at offset zero.
98  *
99  * The file should not have a permanent backing store like a disk,
100  * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
101  *
102  * The file name is deleted from the file system.
103  *
104  * The file is suitable for buffer sharing between processes by
105  * transmitting the file descriptor over Unix sockets using the
106  * SCM_RIGHTS methods.
107  *
108  * If the C library implements posix_fallocate(), it is used to
109  * guarantee that disk space is available for the file at the
110  * given size. If disk space is insufficent, errno is set to ENOSPC.
111  * If posix_fallocate() is not supported, program may receive
112  * SIGBUS on accessing mmap()'ed file contents instead.
113  */
114 static int
os_create_anonymous_file(off_t size)115 os_create_anonymous_file(off_t size)
116 {
117     static const char template[] = "/xwayland-shared-XXXXXX";
118     const char *path;
119     char *name;
120     int fd;
121     int ret;
122 
123     path = getenv("XDG_RUNTIME_DIR");
124     if (!path) {
125         errno = ENOENT;
126         return -1;
127     }
128 
129     name = malloc(strlen(path) + sizeof(template));
130     if (!name)
131         return -1;
132 
133     strcpy(name, path);
134     strcat(name, template);
135 
136     fd = create_tmpfile_cloexec(name);
137 
138     free(name);
139 
140     if (fd < 0)
141         return -1;
142 
143 #ifdef HAVE_POSIX_FALLOCATE
144     /*
145      * posix_fallocate does an explicit rollback if it gets EINTR.
146      * Temporarily block signals to allow the call to succeed on
147      * slow systems where the smart scheduler's SIGALRM prevents
148      * large allocation attempts from ever succeeding.
149      */
150     OsBlockSignals();
151     do {
152         ret = posix_fallocate(fd, 0, size);
153     } while (ret == EINTR);
154     OsReleaseSignals();
155 
156     if (ret != 0) {
157         close(fd);
158         errno = ret;
159         return -1;
160     }
161 #else
162     do {
163         ret = ftruncate(fd, size);
164     } while (ret == -1 && errno == EINTR);
165 
166     if (ret < 0) {
167         close(fd);
168         return -1;
169     }
170 #endif
171 
172     return fd;
173 }
174 
175 static uint32_t
shm_format_for_depth(int depth)176 shm_format_for_depth(int depth)
177 {
178     switch (depth) {
179     case 32:
180         return WL_SHM_FORMAT_ARGB8888;
181     case 24:
182     default:
183         return WL_SHM_FORMAT_XRGB8888;
184 #ifdef WL_SHM_FORMAT_RGB565
185     case 16:
186         /* XXX: Check run-time protocol version too */
187         return WL_SHM_FORMAT_RGB565;
188 #endif
189     }
190 }
191 
192 PixmapPtr
xwl_shm_create_pixmap(ScreenPtr screen,int width,int height,int depth,unsigned int hint)193 xwl_shm_create_pixmap(ScreenPtr screen,
194                       int width, int height, int depth, unsigned int hint)
195 {
196     struct xwl_screen *xwl_screen = xwl_screen_get(screen);
197     struct xwl_pixmap *xwl_pixmap;
198     struct wl_shm_pool *pool;
199     PixmapPtr pixmap;
200     size_t size, stride;
201     uint32_t format;
202     int fd;
203 
204     if (hint == CREATE_PIXMAP_USAGE_GLYPH_PICTURE ||
205         (width == 0 && height == 0) || depth < 15)
206         return fbCreatePixmap(screen, width, height, depth, hint);
207 
208     pixmap = fbCreatePixmap(screen, 0, 0, depth, hint);
209     if (!pixmap)
210         return NULL;
211 
212     xwl_pixmap = malloc(sizeof *xwl_pixmap);
213     if (xwl_pixmap == NULL)
214         goto err_destroy_pixmap;
215 
216     stride = PixmapBytePad(width, depth);
217     size = stride * height;
218     xwl_pixmap->buffer = NULL;
219     xwl_pixmap->size = size;
220     fd = os_create_anonymous_file(size);
221     if (fd < 0)
222         goto err_free_xwl_pixmap;
223 
224     xwl_pixmap->data = mmap(NULL, size, PROT_READ | PROT_WRITE,
225                                   MAP_SHARED, fd, 0);
226     if (xwl_pixmap->data == MAP_FAILED)
227         goto err_close_fd;
228 
229     if (!(*screen->ModifyPixmapHeader) (pixmap, width, height, depth,
230                                         BitsPerPixel(depth),
231                                         stride, xwl_pixmap->data))
232         goto err_munmap;
233 
234     format = shm_format_for_depth(pixmap->drawable.depth);
235     pool = wl_shm_create_pool(xwl_screen->shm, fd, xwl_pixmap->size);
236     xwl_pixmap->buffer = wl_shm_pool_create_buffer(pool, 0,
237                                                    pixmap->drawable.width,
238                                                    pixmap->drawable.height,
239                                                    pixmap->devKind, format);
240     wl_shm_pool_destroy(pool);
241     close(fd);
242 
243     xwl_pixmap_set_private(pixmap, xwl_pixmap);
244 
245     return pixmap;
246 
247  err_munmap:
248     munmap(xwl_pixmap->data, size);
249  err_close_fd:
250     close(fd);
251  err_free_xwl_pixmap:
252     free(xwl_pixmap);
253  err_destroy_pixmap:
254     fbDestroyPixmap(pixmap);
255 
256     return NULL;
257 }
258 
259 Bool
xwl_shm_destroy_pixmap(PixmapPtr pixmap)260 xwl_shm_destroy_pixmap(PixmapPtr pixmap)
261 {
262     struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
263 
264     if (xwl_pixmap && pixmap->refcnt == 1) {
265         if (xwl_pixmap->buffer)
266             wl_buffer_destroy(xwl_pixmap->buffer);
267         munmap(xwl_pixmap->data, xwl_pixmap->size);
268         free(xwl_pixmap);
269     }
270 
271     return fbDestroyPixmap(pixmap);
272 }
273 
274 struct wl_buffer *
xwl_shm_pixmap_get_wl_buffer(PixmapPtr pixmap)275 xwl_shm_pixmap_get_wl_buffer(PixmapPtr pixmap)
276 {
277     return xwl_pixmap_get(pixmap)->buffer;
278 }
279 
280 Bool
xwl_shm_create_screen_resources(ScreenPtr screen)281 xwl_shm_create_screen_resources(ScreenPtr screen)
282 {
283     struct xwl_screen *xwl_screen = xwl_screen_get(screen);
284     int ret;
285 
286     screen->CreateScreenResources = xwl_screen->CreateScreenResources;
287     ret = (*screen->CreateScreenResources) (screen);
288     xwl_screen->CreateScreenResources = screen->CreateScreenResources;
289     screen->CreateScreenResources = xwl_shm_create_screen_resources;
290 
291     if (!ret)
292         return ret;
293 
294     if (xwl_screen->rootless)
295         screen->devPrivate =
296             fbCreatePixmap(screen, 0, 0, screen->rootDepth, 0);
297     else
298         screen->devPrivate =
299             xwl_shm_create_pixmap(screen, screen->width, screen->height,
300                                   screen->rootDepth,
301                                   CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
302 
303     SetRootClip(screen, xwl_screen->root_clip_mode);
304 
305     return screen->devPrivate != NULL;
306 }
307