1*84e872a0SLloyd Pique /*
2*84e872a0SLloyd Pique * Copyright © 2012 Collabora, Ltd.
3*84e872a0SLloyd Pique *
4*84e872a0SLloyd Pique * Permission is hereby granted, free of charge, to any person obtaining
5*84e872a0SLloyd Pique * a copy of this software and associated documentation files (the
6*84e872a0SLloyd Pique * "Software"), to deal in the Software without restriction, including
7*84e872a0SLloyd Pique * without limitation the rights to use, copy, modify, merge, publish,
8*84e872a0SLloyd Pique * distribute, sublicense, and/or sell copies of the Software, and to
9*84e872a0SLloyd Pique * permit persons to whom the Software is furnished to do so, subject to
10*84e872a0SLloyd Pique * the following conditions:
11*84e872a0SLloyd Pique *
12*84e872a0SLloyd Pique * The above copyright notice and this permission notice (including the
13*84e872a0SLloyd Pique * next paragraph) shall be included in all copies or substantial
14*84e872a0SLloyd Pique * portions of the Software.
15*84e872a0SLloyd Pique *
16*84e872a0SLloyd Pique * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17*84e872a0SLloyd Pique * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*84e872a0SLloyd Pique * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19*84e872a0SLloyd Pique * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20*84e872a0SLloyd Pique * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21*84e872a0SLloyd Pique * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22*84e872a0SLloyd Pique * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23*84e872a0SLloyd Pique * SOFTWARE.
24*84e872a0SLloyd Pique */
25*84e872a0SLloyd Pique
26*84e872a0SLloyd Pique #define _GNU_SOURCE
27*84e872a0SLloyd Pique
28*84e872a0SLloyd Pique #include "config.h"
29*84e872a0SLloyd Pique
30*84e872a0SLloyd Pique #include <sys/types.h>
31*84e872a0SLloyd Pique #include <unistd.h>
32*84e872a0SLloyd Pique #include <fcntl.h>
33*84e872a0SLloyd Pique #include <errno.h>
34*84e872a0SLloyd Pique #include <signal.h>
35*84e872a0SLloyd Pique #include <string.h>
36*84e872a0SLloyd Pique #include <stdio.h>
37*84e872a0SLloyd Pique #include <stdlib.h>
38*84e872a0SLloyd Pique
39*84e872a0SLloyd Pique #ifdef HAVE_MEMFD_CREATE
40*84e872a0SLloyd Pique #include <sys/mman.h>
41*84e872a0SLloyd Pique #endif
42*84e872a0SLloyd Pique
43*84e872a0SLloyd Pique #include "os-compatibility.h"
44*84e872a0SLloyd Pique
45*84e872a0SLloyd Pique #ifndef HAVE_MKOSTEMP
46*84e872a0SLloyd Pique static int
set_cloexec_or_close(int fd)47*84e872a0SLloyd Pique set_cloexec_or_close(int fd)
48*84e872a0SLloyd Pique {
49*84e872a0SLloyd Pique long flags;
50*84e872a0SLloyd Pique
51*84e872a0SLloyd Pique if (fd == -1)
52*84e872a0SLloyd Pique return -1;
53*84e872a0SLloyd Pique
54*84e872a0SLloyd Pique flags = fcntl(fd, F_GETFD);
55*84e872a0SLloyd Pique if (flags == -1)
56*84e872a0SLloyd Pique goto err;
57*84e872a0SLloyd Pique
58*84e872a0SLloyd Pique if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
59*84e872a0SLloyd Pique goto err;
60*84e872a0SLloyd Pique
61*84e872a0SLloyd Pique return fd;
62*84e872a0SLloyd Pique
63*84e872a0SLloyd Pique err:
64*84e872a0SLloyd Pique close(fd);
65*84e872a0SLloyd Pique return -1;
66*84e872a0SLloyd Pique }
67*84e872a0SLloyd Pique #endif
68*84e872a0SLloyd Pique
69*84e872a0SLloyd Pique static int
create_tmpfile_cloexec(char * tmpname)70*84e872a0SLloyd Pique create_tmpfile_cloexec(char *tmpname)
71*84e872a0SLloyd Pique {
72*84e872a0SLloyd Pique int fd;
73*84e872a0SLloyd Pique
74*84e872a0SLloyd Pique #ifdef HAVE_MKOSTEMP
75*84e872a0SLloyd Pique fd = mkostemp(tmpname, O_CLOEXEC);
76*84e872a0SLloyd Pique if (fd >= 0)
77*84e872a0SLloyd Pique unlink(tmpname);
78*84e872a0SLloyd Pique #else
79*84e872a0SLloyd Pique fd = mkstemp(tmpname);
80*84e872a0SLloyd Pique if (fd >= 0) {
81*84e872a0SLloyd Pique fd = set_cloexec_or_close(fd);
82*84e872a0SLloyd Pique unlink(tmpname);
83*84e872a0SLloyd Pique }
84*84e872a0SLloyd Pique #endif
85*84e872a0SLloyd Pique
86*84e872a0SLloyd Pique return fd;
87*84e872a0SLloyd Pique }
88*84e872a0SLloyd Pique
89*84e872a0SLloyd Pique /*
90*84e872a0SLloyd Pique * Create a new, unique, anonymous file of the given size, and
91*84e872a0SLloyd Pique * return the file descriptor for it. The file descriptor is set
92*84e872a0SLloyd Pique * CLOEXEC. The file is immediately suitable for mmap()'ing
93*84e872a0SLloyd Pique * the given size at offset zero.
94*84e872a0SLloyd Pique *
95*84e872a0SLloyd Pique * The file should not have a permanent backing store like a disk,
96*84e872a0SLloyd Pique * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
97*84e872a0SLloyd Pique *
98*84e872a0SLloyd Pique * The file name is deleted from the file system.
99*84e872a0SLloyd Pique *
100*84e872a0SLloyd Pique * The file is suitable for buffer sharing between processes by
101*84e872a0SLloyd Pique * transmitting the file descriptor over Unix sockets using the
102*84e872a0SLloyd Pique * SCM_RIGHTS methods.
103*84e872a0SLloyd Pique *
104*84e872a0SLloyd Pique * If the C library implements posix_fallocate(), it is used to
105*84e872a0SLloyd Pique * guarantee that disk space is available for the file at the
106*84e872a0SLloyd Pique * given size. If disk space is insufficient, errno is set to ENOSPC.
107*84e872a0SLloyd Pique * If posix_fallocate() is not supported, program may receive
108*84e872a0SLloyd Pique * SIGBUS on accessing mmap()'ed file contents instead.
109*84e872a0SLloyd Pique *
110*84e872a0SLloyd Pique * If the C library implements memfd_create(), it is used to create the
111*84e872a0SLloyd Pique * file purely in memory, without any backing file name on the file
112*84e872a0SLloyd Pique * system, and then sealing off the possibility of shrinking it. This
113*84e872a0SLloyd Pique * can then be checked before accessing mmap()'ed file contents, to
114*84e872a0SLloyd Pique * make sure SIGBUS can't happen. It also avoids requiring
115*84e872a0SLloyd Pique * XDG_RUNTIME_DIR.
116*84e872a0SLloyd Pique */
117*84e872a0SLloyd Pique int
os_create_anonymous_file(off_t size)118*84e872a0SLloyd Pique os_create_anonymous_file(off_t size)
119*84e872a0SLloyd Pique {
120*84e872a0SLloyd Pique static const char template[] = "/wayland-cursor-shared-XXXXXX";
121*84e872a0SLloyd Pique const char *path;
122*84e872a0SLloyd Pique char *name;
123*84e872a0SLloyd Pique size_t name_size;
124*84e872a0SLloyd Pique int fd;
125*84e872a0SLloyd Pique
126*84e872a0SLloyd Pique #ifdef HAVE_MEMFD_CREATE
127*84e872a0SLloyd Pique fd = memfd_create("wayland-cursor", MFD_CLOEXEC | MFD_ALLOW_SEALING);
128*84e872a0SLloyd Pique if (fd >= 0) {
129*84e872a0SLloyd Pique /* We can add this seal before calling posix_fallocate(), as
130*84e872a0SLloyd Pique * the file is currently zero-sized anyway.
131*84e872a0SLloyd Pique *
132*84e872a0SLloyd Pique * There is also no need to check for the return value, we
133*84e872a0SLloyd Pique * couldn't do anything with it anyway.
134*84e872a0SLloyd Pique */
135*84e872a0SLloyd Pique fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
136*84e872a0SLloyd Pique } else
137*84e872a0SLloyd Pique #endif
138*84e872a0SLloyd Pique {
139*84e872a0SLloyd Pique path = getenv("XDG_RUNTIME_DIR");
140*84e872a0SLloyd Pique if (!path || path[0] != '/') {
141*84e872a0SLloyd Pique errno = ENOENT;
142*84e872a0SLloyd Pique return -1;
143*84e872a0SLloyd Pique }
144*84e872a0SLloyd Pique
145*84e872a0SLloyd Pique name_size = strlen(path) + sizeof(template);
146*84e872a0SLloyd Pique name = malloc(name_size);
147*84e872a0SLloyd Pique if (!name)
148*84e872a0SLloyd Pique return -1;
149*84e872a0SLloyd Pique
150*84e872a0SLloyd Pique snprintf(name, name_size, "%s%s", path, template);
151*84e872a0SLloyd Pique
152*84e872a0SLloyd Pique fd = create_tmpfile_cloexec(name);
153*84e872a0SLloyd Pique
154*84e872a0SLloyd Pique free(name);
155*84e872a0SLloyd Pique
156*84e872a0SLloyd Pique if (fd < 0)
157*84e872a0SLloyd Pique return -1;
158*84e872a0SLloyd Pique }
159*84e872a0SLloyd Pique
160*84e872a0SLloyd Pique if (os_resize_anonymous_file(fd, size) < 0) {
161*84e872a0SLloyd Pique close(fd);
162*84e872a0SLloyd Pique return -1;
163*84e872a0SLloyd Pique }
164*84e872a0SLloyd Pique
165*84e872a0SLloyd Pique return fd;
166*84e872a0SLloyd Pique }
167*84e872a0SLloyd Pique
168*84e872a0SLloyd Pique int
os_resize_anonymous_file(int fd,off_t size)169*84e872a0SLloyd Pique os_resize_anonymous_file(int fd, off_t size)
170*84e872a0SLloyd Pique {
171*84e872a0SLloyd Pique #ifdef HAVE_POSIX_FALLOCATE
172*84e872a0SLloyd Pique sigset_t mask;
173*84e872a0SLloyd Pique sigset_t old_mask;
174*84e872a0SLloyd Pique
175*84e872a0SLloyd Pique /*
176*84e872a0SLloyd Pique * posix_fallocate() might be interrupted, so we need to check
177*84e872a0SLloyd Pique * for EINTR and retry in that case.
178*84e872a0SLloyd Pique * However, in the presence of an alarm, the interrupt may trigger
179*84e872a0SLloyd Pique * repeatedly and prevent a large posix_fallocate() to ever complete
180*84e872a0SLloyd Pique * successfully, so we need to first block SIGALRM to prevent
181*84e872a0SLloyd Pique * this.
182*84e872a0SLloyd Pique */
183*84e872a0SLloyd Pique sigemptyset(&mask);
184*84e872a0SLloyd Pique sigaddset(&mask, SIGALRM);
185*84e872a0SLloyd Pique sigprocmask(SIG_BLOCK, &mask, &old_mask);
186*84e872a0SLloyd Pique /*
187*84e872a0SLloyd Pique * Filesystems that do not support fallocate will return EINVAL or
188*84e872a0SLloyd Pique * EOPNOTSUPP. In this case we need to fall back to ftruncate
189*84e872a0SLloyd Pique */
190*84e872a0SLloyd Pique do {
191*84e872a0SLloyd Pique errno = posix_fallocate(fd, 0, size);
192*84e872a0SLloyd Pique } while (errno == EINTR);
193*84e872a0SLloyd Pique sigprocmask(SIG_SETMASK, &old_mask, NULL);
194*84e872a0SLloyd Pique if (errno == 0)
195*84e872a0SLloyd Pique return 0;
196*84e872a0SLloyd Pique else if (errno != EINVAL && errno != EOPNOTSUPP)
197*84e872a0SLloyd Pique return -1;
198*84e872a0SLloyd Pique #endif
199*84e872a0SLloyd Pique if (ftruncate(fd, size) < 0)
200*84e872a0SLloyd Pique return -1;
201*84e872a0SLloyd Pique
202*84e872a0SLloyd Pique return 0;
203*84e872a0SLloyd Pique }
204