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