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 #define _GNU_SOURCE
27 
28 #include "config.h"
29 
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <stdlib.h>
36 
37 #ifdef HAVE_MEMFD_CREATE
38 #include <sys/mman.h>
39 #endif
40 
41 #include "os-compatibility.h"
42 
43 #ifndef HAVE_MKOSTEMP
44 static int
set_cloexec_or_close(int fd)45 set_cloexec_or_close(int fd)
46 {
47 	long flags;
48 
49 	if (fd == -1)
50 		return -1;
51 
52 	flags = fcntl(fd, F_GETFD);
53 	if (flags == -1)
54 		goto err;
55 
56 	if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
57 		goto err;
58 
59 	return fd;
60 
61 err:
62 	close(fd);
63 	return -1;
64 }
65 #endif
66 
67 static int
create_tmpfile_cloexec(char * tmpname)68 create_tmpfile_cloexec(char *tmpname)
69 {
70 	int fd;
71 
72 #ifdef HAVE_MKOSTEMP
73 	fd = mkostemp(tmpname, O_CLOEXEC);
74 	if (fd >= 0)
75 		unlink(tmpname);
76 #else
77 	fd = mkstemp(tmpname);
78 	if (fd >= 0) {
79 		fd = set_cloexec_or_close(fd);
80 		unlink(tmpname);
81 	}
82 #endif
83 
84 	return fd;
85 }
86 
87 /*
88  * Create a new, unique, anonymous file of the given size, and
89  * return the file descriptor for it. The file descriptor is set
90  * CLOEXEC. The file is immediately suitable for mmap()'ing
91  * the given size at offset zero.
92  *
93  * The file should not have a permanent backing store like a disk,
94  * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
95  *
96  * The file name is deleted from the file system.
97  *
98  * The file is suitable for buffer sharing between processes by
99  * transmitting the file descriptor over Unix sockets using the
100  * SCM_RIGHTS methods.
101  *
102  * If the C library implements posix_fallocate(), it is used to
103  * guarantee that disk space is available for the file at the
104  * given size. If disk space is insufficient, errno is set to ENOSPC.
105  * If posix_fallocate() is not supported, program may receive
106  * SIGBUS on accessing mmap()'ed file contents instead.
107  *
108  * If the C library implements memfd_create(), it is used to create the
109  * file purely in memory, without any backing file name on the file
110  * system, and then sealing off the possibility of shrinking it.  This
111  * can then be checked before accessing mmap()'ed file contents, to
112  * make sure SIGBUS can't happen.  It also avoids requiring
113  * XDG_RUNTIME_DIR.
114  */
115 int
os_create_anonymous_file(off_t size)116 os_create_anonymous_file(off_t size)
117 {
118 	static const char template[] = "/wayland-cursor-shared-XXXXXX";
119 	const char *path;
120 	char *name;
121 	int fd;
122 
123 #ifdef HAVE_MEMFD_CREATE
124 	fd = memfd_create("wayland-cursor", MFD_CLOEXEC | MFD_ALLOW_SEALING);
125 	if (fd >= 0) {
126 		/* We can add this seal before calling posix_fallocate(), as
127 		 * the file is currently zero-sized anyway.
128 		 *
129 		 * There is also no need to check for the return value, we
130 		 * couldn't do anything with it anyway.
131 		 */
132 		fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
133 	} else
134 #endif
135 	{
136 		path = getenv("XDG_RUNTIME_DIR");
137 		if (!path) {
138 			errno = ENOENT;
139 			return -1;
140 		}
141 
142 		name = malloc(strlen(path) + sizeof(template));
143 		if (!name)
144 			return -1;
145 
146 		strcpy(name, path);
147 		strcat(name, template);
148 
149 		fd = create_tmpfile_cloexec(name);
150 
151 		free(name);
152 
153 		if (fd < 0)
154 			return -1;
155 	}
156 
157 	if (os_resize_anonymous_file(fd, size) < 0) {
158 		close(fd);
159 		return -1;
160 	}
161 
162 	return fd;
163 }
164 
165 int
os_resize_anonymous_file(int fd,off_t size)166 os_resize_anonymous_file(int fd, off_t size)
167 {
168 #ifdef HAVE_POSIX_FALLOCATE
169 	/*
170 	 * Filesystems that do support fallocate will return EINVAL or
171 	 * EOPNOTSUPP. In this case we need to fall back to ftruncate
172 	 */
173 	errno = posix_fallocate(fd, 0, size);
174 	if (errno == 0)
175 		return 0;
176 	else if (errno != EINVAL && errno != EOPNOTSUPP)
177 		return -1;
178 #endif
179 	if (ftruncate(fd, size) < 0)
180 		return -1;
181 
182 	return 0;
183 }
184