FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
uwac-os.c
1/*
2 * Copyright © 2012 Collabora, Ltd.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23/*
24 * This file is an adaptation of src/wayland-os.h from the wayland project and
25 * shared/os-compatiblity.h from the weston project.
26 *
27 * Functions have been renamed just to prevent name clashes.
28 */
29
30#if defined(__clang__)
31#pragma clang diagnostic push
32#pragma clang diagnostic ignored "-Wreserved-id-macro"
33#endif
34
35#define _GNU_SOURCE // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
36
37#if defined(__clang__)
38#pragma clang diagnostic pop
39#endif
40
41#if defined(__FreeBSD__) || defined(__DragonFly__)
42#define USE_SHM
43#endif
44
45/* uClibc and uClibc-ng don't provide O_TMPFILE */
46#if !defined(O_TMPFILE) && !defined(__FreeBSD__)
47#define O_TMPFILE (020000000 | O_DIRECTORY)
48#endif
49
50#include <sys/types.h>
51#include <sys/socket.h>
52#ifdef USE_SHM
53#include <sys/mman.h>
54#endif
55#include <unistd.h>
56#include <fcntl.h>
57#include <errno.h>
58#include <stdlib.h>
59#include <stdio.h>
60#include <string.h>
61#include <sys/epoll.h>
62#include <sys/stat.h>
63
64#include <uwac/config.h>
65
66#include "uwac-os.h"
67#include "uwac-utils.h"
68
69static int set_cloexec_or_close(int fd)
70{
71 long flags = 0;
72
73 if (fd == -1)
74 return -1;
75
76 flags = fcntl(fd, F_GETFD);
77
78 if (flags == -1)
79 goto err;
80
81 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
82 goto err;
83
84 return fd;
85err:
86 close(fd);
87 return -1;
88}
89
90int uwac_os_socket_cloexec(int domain, int type, int protocol)
91{
92 int fd = 0;
93 fd = socket(domain, type | SOCK_CLOEXEC, protocol);
94
95 if (fd >= 0)
96 return fd;
97
98 if (errno != EINVAL)
99 return -1;
100
101 fd = socket(domain, type, protocol);
102 return set_cloexec_or_close(fd);
103}
104
105int uwac_os_dupfd_cloexec(int fd, long minfd)
106{
107 int newfd = 0;
108 newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
109
110 if (newfd >= 0)
111 return newfd;
112
113 if (errno != EINVAL)
114 return -1;
115
116 newfd = fcntl(fd, F_DUPFD, minfd);
117 return set_cloexec_or_close(newfd);
118}
119
120static ssize_t recvmsg_cloexec_fallback(int sockfd, struct msghdr* msg, int flags)
121{
122 ssize_t len = 0;
123 struct cmsghdr* cmsg = NULL;
124 unsigned char* data = NULL;
125 int* end = NULL;
126 len = recvmsg(sockfd, msg, flags);
127
128 if (len == -1)
129 return -1;
130
131 if (!msg->msg_control || msg->msg_controllen == 0)
132 return len;
133
134 cmsg = CMSG_FIRSTHDR(msg);
135
136 for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
137 {
138 if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
139 continue;
140
141 data = CMSG_DATA(cmsg);
142 end = (int*)(data + cmsg->cmsg_len - CMSG_LEN(0));
143
144 for (int* fd = (int*)data; fd < end; ++fd)
145 *fd = set_cloexec_or_close(*fd);
146 }
147
148 return len;
149}
150
151ssize_t uwac_os_recvmsg_cloexec(int sockfd, struct msghdr* msg, int flags)
152{
153 ssize_t len = 0;
154 len = recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC);
155
156 if (len >= 0)
157 return len;
158
159 if (errno != EINVAL)
160 return -1;
161
162 return recvmsg_cloexec_fallback(sockfd, msg, flags);
163}
164
165int uwac_os_epoll_create_cloexec(void)
166{
167 int fd = 0;
168#ifdef EPOLL_CLOEXEC
169 fd = epoll_create1(EPOLL_CLOEXEC);
170
171 if (fd >= 0)
172 return fd;
173
174 if (errno != EINVAL)
175 return -1;
176
177#endif
178 fd = epoll_create(1);
179 return set_cloexec_or_close(fd);
180}
181
182static int secure_mkstemp(char* tmpname)
183{
184 const mode_t mask = umask(S_IRWXU);
185 int fd = mkstemp(tmpname);
186 (void)umask(mask);
187 return fd;
188}
189
190static int create_tmpfile_cloexec(char* tmpname)
191{
192 int fd = 0;
193#ifdef USE_SHM
194 fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
195#elif defined(UWAC_HAVE_MKOSTEMP)
196 fd = mkostemp(tmpname, O_CLOEXEC);
197
198 if (fd >= 0)
199 unlink(tmpname);
200
201#else
202 fd = secure_mkstemp(tmpname);
203
204 if (fd >= 0)
205 {
206 fd = set_cloexec_or_close(fd);
207 unlink(tmpname);
208 }
209
210#endif
211 return fd;
212}
213
214/*
215 * Create a new, unique, anonymous file of the given size, and
216 * return the file descriptor for it. The file descriptor is set
217 * CLOEXEC. The file is immediately suitable for mmap()'ing
218 * the given size at offset zero.
219 *
220 * The file should not have a permanent backing store like a disk,
221 * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
222 *
223 * The file name is deleted from the file system.
224 *
225 * The file is suitable for buffer sharing between processes by
226 * transmitting the file descriptor over Unix sockets using the
227 * SCM_RIGHTS methods.
228 *
229 * If the C library implements posix_fallocate(), it is used to
230 * guarantee that disk space is available for the file at the
231 * given size. If disk space is insufficient, errno is set to ENOSPC.
232 * If posix_fallocate() is not supported, program may receive
233 * SIGBUS on accessing mmap()'ed file contents instead.
234 */
235int uwac_create_anonymous_file(off_t size)
236{
237 static const char template[] = "/weston-shared-XXXXXX";
238 size_t length = 0;
239 char* name = NULL;
240 int fd = 0;
241 int ret = 0;
242 // NOLINTNEXTLINE(concurrency-mt-unsafe)
243 const char* path = getenv("XDG_RUNTIME_DIR");
244
245 if (!path)
246 {
247 errno = ENOENT;
248 return -1;
249 }
250
251#ifdef O_TMPFILE
252 fd = open(path, O_TMPFILE | O_RDWR | O_EXCL, 0600);
253#else
254 /*
255 * Some platforms (e.g. FreeBSD) won't support O_TMPFILE and can't
256 * reasonably emulate it at first blush. Opt to make them rely on
257 * the create_tmpfile_cloexec() path instead.
258 */
259 fd = -1;
260#endif
261
262 if (fd < 0)
263 {
264 length = strlen(path) + sizeof(template);
265 name = xmalloc(length);
266
267 if (!name)
268 return -1;
269
270 (void)snprintf(name, length, "%s%s", path, template);
271 fd = create_tmpfile_cloexec(name);
272 free(name);
273 }
274
275 if (fd < 0)
276 return -1;
277
278#ifdef UWAC_HAVE_POSIX_FALLOCATE
279 ret = posix_fallocate(fd, 0, size);
280
281 if (ret != 0)
282 {
283 close(fd);
284 errno = ret;
285 return -1;
286 }
287
288#else
289 ret = ftruncate(fd, size);
290
291 if (ret < 0)
292 {
293 close(fd);
294 return -1;
295 }
296
297#endif
298 return fd;
299}