• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2012 Collabora, Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include "config.h"
27 
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <sys/epoll.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <libweston/zalloc.h>
37 #include <sys/mman.h>
38 
39 #include "os-compatibility.h"
40 
41 #define READONLY_SEALS (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
42 
43 int
os_fd_set_cloexec(int fd)44 os_fd_set_cloexec(int fd)
45 {
46 	long flags;
47 
48 	if (fd == -1)
49 		return -1;
50 
51 	flags = fcntl(fd, F_GETFD);
52 	if (flags == -1)
53 		return -1;
54 
55 	if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
56 		return -1;
57 
58 	return 0;
59 }
60 
61 static int
set_cloexec_or_close(int fd)62 set_cloexec_or_close(int fd)
63 {
64 	if (os_fd_set_cloexec(fd) != 0) {
65 		close(fd);
66 		return -1;
67 	}
68 	return fd;
69 }
70 
71 int
os_socketpair_cloexec(int domain,int type,int protocol,int * sv)72 os_socketpair_cloexec(int domain, int type, int protocol, int *sv)
73 {
74 	int ret;
75 
76 #ifdef SOCK_CLOEXEC
77 	ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv);
78 	if (ret == 0 || errno != EINVAL)
79 		return ret;
80 #endif
81 
82 	ret = socketpair(domain, type, protocol, sv);
83 	if (ret < 0)
84 		return ret;
85 
86 	sv[0] = set_cloexec_or_close(sv[0]);
87 	sv[1] = set_cloexec_or_close(sv[1]);
88 
89 	if (sv[0] != -1 && sv[1] != -1)
90 		return 0;
91 
92 	close(sv[0]);
93 	close(sv[1]);
94 	return -1;
95 }
96 
97 int
os_epoll_create_cloexec(void)98 os_epoll_create_cloexec(void)
99 {
100 	int fd;
101 
102 #ifdef EPOLL_CLOEXEC
103 	fd = epoll_create1(EPOLL_CLOEXEC);
104 	if (fd >= 0)
105 		return fd;
106 	if (errno != EINVAL)
107 		return -1;
108 #endif
109 
110 	fd = epoll_create(1);
111 	return set_cloexec_or_close(fd);
112 }
113 
114 static int
create_tmpfile_cloexec(char * tmpname)115 create_tmpfile_cloexec(char *tmpname)
116 {
117 	int fd;
118 
119 #ifdef HAVE_MKOSTEMP
120 	fd = mkostemp(tmpname, O_CLOEXEC);
121 	if (fd >= 0)
122 		unlink(tmpname);
123 #else
124 	fd = mkstemp(tmpname);
125 	if (fd >= 0) {
126 		fd = set_cloexec_or_close(fd);
127 		unlink(tmpname);
128 	}
129 #endif
130 
131 	return fd;
132 }
133 
134 /*
135  * Create a new, unique, anonymous file of the given size, and
136  * return the file descriptor for it. The file descriptor is set
137  * CLOEXEC. The file is immediately suitable for mmap()'ing
138  * the given size at offset zero.
139  *
140  * The file should not have a permanent backing store like a disk,
141  * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
142  *
143  * The file name is deleted from the file system.
144  *
145  * The file is suitable for buffer sharing between processes by
146  * transmitting the file descriptor over Unix sockets using the
147  * SCM_RIGHTS methods.
148  *
149  * If the C library implements posix_fallocate(), it is used to
150  * guarantee that disk space is available for the file at the
151  * given size. If disk space is insufficient, errno is set to ENOSPC.
152  * If posix_fallocate() is not supported, program may receive
153  * SIGBUS on accessing mmap()'ed file contents instead.
154  *
155  * If the C library implements memfd_create(), it is used to create the
156  * file purely in memory, without any backing file name on the file
157  * system, and then sealing off the possibility of shrinking it.  This
158  * can then be checked before accessing mmap()'ed file contents, to
159  * make sure SIGBUS can't happen.  It also avoids requiring
160  * XDG_RUNTIME_DIR.
161  */
162 int
os_create_anonymous_file(off_t size)163 os_create_anonymous_file(off_t size)
164 {
165 	static const char template[] = "/weston-shared-XXXXXX";
166 	const char *path;
167 	char *name;
168 	int fd;
169 	int ret;
170 
171 #ifdef HAVE_MEMFD_CREATE
172 	fd = memfd_create("weston-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
173 	if (fd >= 0) {
174 		/* We can add this seal before calling posix_fallocate(), as
175 		 * the file is currently zero-sized anyway.
176 		 *
177 		 * There is also no need to check for the return value, we
178 		 * couldn't do anything with it anyway.
179 		 */
180 		fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK);
181 	} else
182 #endif
183 	{
184 		path = getenv("XDG_RUNTIME_DIR");
185 		if (!path) {
186 			errno = ENOENT;
187 			return -1;
188 		}
189 
190 		name = malloc(strlen(path) + sizeof(template));
191 		if (!name)
192 			return -1;
193 
194 		strcpy(name, path);
195 		strcat(name, template);
196 
197 		fd = create_tmpfile_cloexec(name);
198 
199 		free(name);
200 
201 		if (fd < 0)
202 			return -1;
203 	}
204 
205 #ifdef HAVE_POSIX_FALLOCATE
206 	do {
207 		ret = posix_fallocate(fd, 0, size);
208 	} while (ret == EINTR);
209 	if (ret != 0) {
210 		close(fd);
211 		errno = ret;
212 		return -1;
213 	}
214 #else
215 	do {
216 		ret = ftruncate(fd, size);
217 	} while (ret < 0 && errno == EINTR);
218 	if (ret < 0) {
219 		close(fd);
220 		return -1;
221 	}
222 #endif
223 
224 	return fd;
225 }
226 
227 #ifndef HAVE_STRCHRNUL
228 char *
strchrnul(const char * s,int c)229 strchrnul(const char *s, int c)
230 {
231 	while (*s && *s != c)
232 		s++;
233 	return (char *)s;
234 }
235 #endif
236 
237 struct ro_anonymous_file {
238 	int fd;
239 	size_t size;
240 };
241 
242 /** Create a new anonymous read-only file of the given size and the given data
243  *
244  * \param size The size of \p data.
245  * \param data The data of the file with the size \p size.
246  * \return A new \c ro_anonymous_file, or NULL on failure.
247  *
248  * The intended use-case is for sending mid-sized data from the compositor
249  * to clients.
250  * If the function fails errno is set.
251  */
252 struct ro_anonymous_file *
os_ro_anonymous_file_create(size_t size,const char * data)253 os_ro_anonymous_file_create(size_t size,
254 			    const char *data)
255 {
256 	struct ro_anonymous_file *file;
257 	void *map;
258 
259 	file = zalloc(sizeof *file);
260 	if (!file) {
261 		errno = ENOMEM;
262 		return NULL;
263 	}
264 
265 	file->size = size;
266 	file->fd = os_create_anonymous_file(size);
267 	if (file->fd == -1)
268 		goto err_free;
269 
270 	map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, file->fd, 0);
271 	if (map == MAP_FAILED)
272 		goto err_close;
273 
274 	memcpy(map, data, size);
275 
276 	munmap(map, size);
277 
278 #ifdef HAVE_MEMFD_CREATE
279 	/* try to put seals on the file to make it read-only so that we can
280 	 * return the fd later directly when support_shared is not set.
281 	 * os_ro_anonymous_file_get_fd can handle the fd even if it is not
282 	 * sealed read-only and will instead create a new anonymous file on
283 	 * each invocation.
284 	 */
285 	fcntl(file->fd, F_ADD_SEALS, READONLY_SEALS);
286 #endif
287 
288 	return file;
289 
290 err_close:
291 	close(file->fd);
292 err_free:
293 	free(file);
294 	return NULL;
295 }
296 
297 /** Destroy an anonymous read-only file
298  *
299  * \param file The file to destroy.
300  */
301 void
os_ro_anonymous_file_destroy(struct ro_anonymous_file * file)302 os_ro_anonymous_file_destroy(struct ro_anonymous_file *file)
303 {
304 	close(file->fd);
305 	free(file);
306 }
307 
308 /** Get the size of an anonymous read-only file
309  *
310  * \param file The file to get the size of.
311  * \return The size of the file.
312  */
313 size_t
os_ro_anonymous_file_size(struct ro_anonymous_file * file)314 os_ro_anonymous_file_size(struct ro_anonymous_file *file)
315 {
316 	return file->size;
317 }
318 
319 /** Returns a file descriptor for the given file, ready to be send to a client.
320  *
321  * \param file The file for which to get a file descriptor.
322  * \param mapmode Describes the ways in which the returned file descriptor can
323  * be used with mmap.
324  * \return A file descriptor for the given file that can be send to a client
325  * or -1 on failure.
326  *
327  * The returned file descriptor must not be shared between multiple clients.
328  * When \p mapmode is RO_ANONYMOUS_FILE_MAPMODE_PRIVATE the file descriptor is
329  * only guaranteed to be mmapable with \c MAP_PRIVATE, when \p mapmode is
330  * RO_ANONYMOUS_FILE_MAPMODE_SHARED the file descriptor can be mmaped with
331  * either MAP_PRIVATE or MAP_SHARED.
332  * When you're done with the fd you must call \c os_ro_anonymous_file_put_fd
333  * instead of calling \c close.
334  * If the function fails errno is set.
335  */
336 int
os_ro_anonymous_file_get_fd(struct ro_anonymous_file * file,enum ro_anonymous_file_mapmode mapmode)337 os_ro_anonymous_file_get_fd(struct ro_anonymous_file *file,
338 			    enum ro_anonymous_file_mapmode mapmode)
339 {
340 	void *src, *dst;
341 	int fd;
342 
343 #ifdef HAVE_MEMFD_CREATE
344 	int seals = fcntl(file->fd, F_GET_SEALS);
345 
346 	/* file was sealed for read-only and we don't have to support MAP_SHARED
347 	 * so we can simply pass the memfd fd
348 	 */
349 	if (seals != -1 && mapmode == RO_ANONYMOUS_FILE_MAPMODE_PRIVATE &&
350 	    (seals & READONLY_SEALS) == READONLY_SEALS)
351 		return file->fd;
352 #endif
353 
354 	/* for all other cases we create a new anonymous file that can be mapped
355 	 * with MAP_SHARED and copy the contents to it and return that instead
356 	 */
357 	fd = os_create_anonymous_file(file->size);
358 	if (fd == -1)
359 		return fd;
360 
361 	src = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0);
362 	if (src == MAP_FAILED) {
363 		close(fd);
364 		return -1;
365 	}
366 
367 	dst = mmap(NULL, file->size, PROT_WRITE, MAP_SHARED, fd, 0);
368 	if (dst == MAP_FAILED) {
369 		close(fd);
370 		munmap(src, file->size);
371 		return -1;
372 	}
373 
374 	memcpy(dst, src, file->size);
375 	munmap(src, file->size);
376 	munmap(dst, file->size);
377 
378 	return fd;
379 }
380 
381 /** Release a file descriptor returned by \c os_ro_anonymous_file_get_fd
382  *
383  * \param fd A file descriptor returned by \c os_ro_anonymous_file_get_fd.
384  * \return 0 on success, or -1 on failure.
385  *
386  * This function must be called for every file descriptor created with
387  * \c os_ro_anonymous_file_get_fd to not leake any resources.
388  * If the function fails errno is set.
389  */
390 int
os_ro_anonymous_file_put_fd(int fd)391 os_ro_anonymous_file_put_fd(int fd)
392 {
393 #ifdef HAVE_MEMFD_CREATE
394 	int seals = fcntl(fd, F_GET_SEALS);
395 	if (seals == -1 && errno != EINVAL)
396 		return -1;
397 
398 	/* The only case in which we do NOT have to close the file is when the file
399 	 * was sealed for read-only
400 	 */
401 	if (seals != -1 && (seals & READONLY_SEALS) == READONLY_SEALS)
402 		return 0;
403 #endif
404 
405 	close(fd);
406 	return 0;
407 }
408