1 /*
2 * Copyright (C) 2020 Sebastian Wick
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 *
19 * Author: Sebastian Wick <sebastian@sebastianwick.net>
20 */
21
22 #include "config.h"
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <sys/mman.h>
27
28 #include "core/meta-anonymous-file.h"
29
30 struct _MetaAnonymousFile
31 {
32 int fd;
33 size_t size;
34 };
35
36 #define READONLY_SEALS (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)
37
38 static int
create_tmpfile_cloexec(char * tmpname)39 create_tmpfile_cloexec (char *tmpname)
40 {
41 int fd;
42
43 #if defined(HAVE_MKOSTEMP)
44 fd = mkostemp (tmpname, O_CLOEXEC);
45 if (fd >= 0)
46 unlink (tmpname);
47 #else
48 fd = mkstemp (tmpname);
49 if (fd >= 0)
50 {
51 long flags;
52
53 unlink (tmpname);
54
55 flags = fcntl (fd, F_GETFD);
56 if (flags == -1 ||
57 fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
58 {
59 close (fd);
60 return -1;
61 }
62 }
63 #endif
64
65 return fd;
66 }
67
68 /*
69 * Create a new, unique, anonymous file of the given size, and
70 * return the file descriptor for it. The file descriptor is set
71 * CLOEXEC. The file is immediately suitable for mmap()'ing
72 * the given size at offset zero.
73 *
74 * The file should not have a permanent backing store like a disk,
75 * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
76 *
77 * The file name is deleted from the file system.
78 *
79 * The file is suitable for buffer sharing between processes by
80 * transmitting the file descriptor over Unix sockets using the
81 * SCM_RIGHTS methods.
82 *
83 * If the C library implements posix_fallocate(), it is used to
84 * guarantee that disk space is available for the file at the
85 * given size. If disk space is insufficient, errno is set to ENOSPC.
86 * If posix_fallocate() is not supported, program may receive
87 * SIGBUS on accessing mmap()'ed file contents instead.
88 *
89 * If the C library implements memfd_create(), it is used to create the
90 * file purely in memory, without any backing file name on the file
91 * system, and then sealing off the possibility of shrinking it. This
92 * can then be checked before accessing mmap()'ed file contents, to make
93 * sure SIGBUS can't happen. It also avoids requiring XDG_RUNTIME_DIR.
94 */
95 static int
create_anonymous_file(off_t size)96 create_anonymous_file (off_t size)
97 {
98 int fd, ret;
99
100 #if defined(HAVE_MEMFD_CREATE)
101 fd = memfd_create ("mutter-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
102 if (fd >= 0)
103 {
104 /* We can add this seal before calling posix_fallocate(), as
105 * the file is currently zero-sized anyway.
106 *
107 * There is also no need to check for the return value, we
108 * couldn't do anything with it anyway.
109 */
110 fcntl (fd, F_ADD_SEALS, F_SEAL_SHRINK);
111 }
112 else
113 #endif
114 {
115 static const char template[] = "/mutter-shared-XXXXXX";
116 const char *path;
117 char *name;
118
119 path = getenv ("XDG_RUNTIME_DIR");
120 if (!path)
121 {
122 errno = ENOENT;
123 return -1;
124 }
125
126 name = g_malloc (strlen (path) + sizeof (template));
127 if (!name)
128 return -1;
129
130 strcpy (name, path);
131 strcat (name, template);
132
133 fd = create_tmpfile_cloexec (name);
134
135 g_free (name);
136
137 if (fd < 0)
138 return -1;
139 }
140
141 #if defined(HAVE_POSIX_FALLOCATE)
142 do
143 {
144 ret = posix_fallocate (fd, 0, size);
145 }
146 while (ret == EINTR);
147
148 if (ret != 0)
149 {
150 close (fd);
151 errno = ret;
152 return -1;
153 }
154 #else
155 do
156 {
157 ret = ftruncate (fd, size);
158 }
159 while (ret < 0 && errno == EINTR);
160
161 if (ret < 0)
162 {
163 close (fd);
164 return -1;
165 }
166 #endif
167
168 return fd;
169 }
170
171 /**
172 * meta_anonymous_file_new: (skip)
173 * @size: The size of @data
174 * @data: The data of the file with the size @size
175 *
176 * Create a new anonymous read-only file of the given size and the given data
177 * The intended use-case is for sending mid-sized data from the compositor
178 * to clients.
179 *
180 * When done, free the data using meta_anonymous_file_free().
181 *
182 * If this function fails errno is set.
183 *
184 * Returns: The newly created #MetaAnonymousFile, or NULL on failure. Use
185 * meta_anonymous_file_free() to free the resources when done.
186 */
187 MetaAnonymousFile *
meta_anonymous_file_new(size_t size,const uint8_t * data)188 meta_anonymous_file_new (size_t size,
189 const uint8_t *data)
190 {
191 MetaAnonymousFile *file;
192 void *map;
193
194 file = g_malloc0 (sizeof *file);
195 if (!file)
196 {
197 errno = ENOMEM;
198 return NULL;
199 }
200
201 file->size = size;
202 file->fd = create_anonymous_file (size);
203 if (file->fd == -1)
204 goto err_free;
205
206 map = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, file->fd, 0);
207 if (map == MAP_FAILED)
208 goto err_close;
209
210 memcpy (map, data, size);
211
212 munmap (map, size);
213
214 #if defined(HAVE_MEMFD_CREATE)
215 /* try to put seals on the file to make it read-only so that we can
216 * return the fd later directly when MAPMODE_SHARED is not set.
217 * meta_anonymous_file_open_fd can handle the fd even if it is not
218 * sealed read-only and will instead create a new anonymous file on
219 * each invocation.
220 */
221 fcntl (file->fd, F_ADD_SEALS, READONLY_SEALS);
222 #endif
223
224 return file;
225
226 err_close:
227 close (file->fd);
228 err_free:
229 g_free (file);
230 return NULL;
231 }
232
233
234 /**
235 * meta_anonymous_file_free: (skip)
236 * @file: the #MetaAnonymousFile
237 *
238 * Free the resources used by an anonymous read-only file.
239 */
240 void
meta_anonymous_file_free(MetaAnonymousFile * file)241 meta_anonymous_file_free (MetaAnonymousFile *file)
242 {
243 close (file->fd);
244 g_free (file);
245 }
246
247 /**
248 * meta_anonymous_file_size: (skip)
249 * @file: the #MetaAnonymousFile
250 *
251 * Get the size of an anonymous read-only file.
252 *
253 * Returns: The size of the anonymous read-only file.
254 */
255 size_t
meta_anonymous_file_size(MetaAnonymousFile * file)256 meta_anonymous_file_size (MetaAnonymousFile *file)
257 {
258 return file->size;
259 }
260
261 /**
262 * meta_anonymous_file_open_fd: (skip)
263 * @file: the #MetaAnonymousFile to get a file descriptor for
264 * @mapmode: describes the ways in which the returned file descriptor can
265 * be used with mmap
266 *
267 * Returns a file descriptor for the given file, ready to be sent to a client.
268 * The returned file descriptor must not be shared between multiple clients.
269 * If @mapmode is %META_ANONYMOUS_FILE_MAPMODE_PRIVATE the file descriptor is
270 * only guaranteed to be mmapable with MAP_PRIVATE. If @mapmode is
271 * %META_ANONYMOUS_FILE_MAPMODE_SHARED the file descriptor can be mmaped with
272 * either MAP_PRIVATE or MAP_SHARED.
273 *
274 * In case %META_ANONYMOUS_FILE_MAPMODE_PRIVATE is used, it is important to
275 * only read the returned fd using mmap() since using read() will move the
276 * read cursor of the fd and thus may cause read() calls on other returned
277 * fds to fail.
278 *
279 * When done using the fd, it is required to call meta_anonymous_file_close_fd()
280 * instead of close().
281 *
282 * If this function fails errno is set.
283 *
284 * Returns: A file descriptor for the given file that can be sent to a client
285 * or -1 on failure. Use meta_anonymous_file_close_fd() to release the fd
286 * when done.
287 */
288 int
meta_anonymous_file_open_fd(MetaAnonymousFile * file,MetaAnonymousFileMapmode mapmode)289 meta_anonymous_file_open_fd (MetaAnonymousFile *file,
290 MetaAnonymousFileMapmode mapmode)
291 {
292 void *src, *dst;
293 int fd;
294
295 #if defined(HAVE_MEMFD_CREATE)
296 int seals;
297
298 seals = fcntl (file->fd, F_GET_SEALS);
299
300 /* file was sealed for read-only and we don't have to support MAP_SHARED
301 * so we can simply pass the memfd fd
302 */
303 if (seals != -1 && mapmode == META_ANONYMOUS_FILE_MAPMODE_PRIVATE &&
304 (seals & READONLY_SEALS) == READONLY_SEALS)
305 return file->fd;
306 #endif
307
308 /* for all other cases we create a new anonymous file that can be mapped
309 * with MAP_SHARED and copy the contents to it and return that instead
310 */
311 fd = create_anonymous_file (file->size);
312 if (fd == -1)
313 return fd;
314
315 src = mmap (NULL, file->size, PROT_READ, MAP_PRIVATE, file->fd, 0);
316 if (src == MAP_FAILED)
317 {
318 close (fd);
319 return -1;
320 }
321
322 dst = mmap (NULL, file->size, PROT_WRITE, MAP_SHARED, fd, 0);
323 if (dst == MAP_FAILED)
324 {
325 close (fd);
326 munmap (src, file->size);
327 return -1;
328 }
329
330 memcpy (dst, src, file->size);
331 munmap (src, file->size);
332 munmap (dst, file->size);
333
334 return fd;
335 }
336
337 /**
338 * meta_anonymous_file_close_fd: (skip)
339 * @fd: A file descriptor obtained using meta_anonymous_file_open_fd()
340 *
341 * Release a file descriptor returned by meta_anonymous_file_open_fd().
342 * This function must be called for every file descriptor created with
343 * meta_anonymous_file_open_fd() to not leak any resources.
344 *
345 * If this function fails errno is set.
346 */
347 void
meta_anonymous_file_close_fd(int fd)348 meta_anonymous_file_close_fd (int fd)
349 {
350 #if defined(HAVE_MEMFD_CREATE)
351 int seals;
352
353 seals = fcntl (fd, F_GET_SEALS);
354 if (seals == -1 && errno != EINVAL)
355 {
356 g_warning ("Reading seals of anonymous file %d failed", fd);
357 return;
358 }
359
360 /* The only case in which we do NOT have to close the file is when the file
361 * was sealed for read-only
362 */
363 if (seals != -1 && (seals & READONLY_SEALS) == READONLY_SEALS)
364 return;
365 #endif
366
367 close (fd);
368 }
369