FreeRDP
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 
63 #include <uwac/config.h>
64 
65 #include "uwac-os.h"
66 #include "uwac-utils.h"
67 
68 static int set_cloexec_or_close(int fd)
69 {
70  long flags = 0;
71 
72  if (fd == -1)
73  return -1;
74 
75  flags = fcntl(fd, F_GETFD);
76 
77  if (flags == -1)
78  goto err;
79 
80  if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
81  goto err;
82 
83  return fd;
84 err:
85  close(fd);
86  return -1;
87 }
88 
89 int uwac_os_socket_cloexec(int domain, int type, int protocol)
90 {
91  int fd = 0;
92  fd = socket(domain, type | SOCK_CLOEXEC, protocol);
93 
94  if (fd >= 0)
95  return fd;
96 
97  if (errno != EINVAL)
98  return -1;
99 
100  fd = socket(domain, type, protocol);
101  return set_cloexec_or_close(fd);
102 }
103 
104 int uwac_os_dupfd_cloexec(int fd, long minfd)
105 {
106  int newfd = 0;
107  newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd);
108 
109  if (newfd >= 0)
110  return newfd;
111 
112  if (errno != EINVAL)
113  return -1;
114 
115  newfd = fcntl(fd, F_DUPFD, minfd);
116  return set_cloexec_or_close(newfd);
117 }
118 
119 static ssize_t recvmsg_cloexec_fallback(int sockfd, struct msghdr* msg, int flags)
120 {
121  ssize_t len = 0;
122  struct cmsghdr* cmsg = NULL;
123  unsigned char* data = NULL;
124  int* end = NULL;
125  len = recvmsg(sockfd, msg, flags);
126 
127  if (len == -1)
128  return -1;
129 
130  if (!msg->msg_control || msg->msg_controllen == 0)
131  return len;
132 
133  cmsg = CMSG_FIRSTHDR(msg);
134 
135  for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
136  {
137  if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
138  continue;
139 
140  data = CMSG_DATA(cmsg);
141  end = (int*)(data + cmsg->cmsg_len - CMSG_LEN(0));
142 
143  for (int* fd = (int*)data; fd < end; ++fd)
144  *fd = set_cloexec_or_close(*fd);
145  }
146 
147  return len;
148 }
149 
150 ssize_t uwac_os_recvmsg_cloexec(int sockfd, struct msghdr* msg, int flags)
151 {
152  ssize_t len = 0;
153  len = recvmsg(sockfd, msg, flags | MSG_CMSG_CLOEXEC);
154 
155  if (len >= 0)
156  return len;
157 
158  if (errno != EINVAL)
159  return -1;
160 
161  return recvmsg_cloexec_fallback(sockfd, msg, flags);
162 }
163 
164 int uwac_os_epoll_create_cloexec(void)
165 {
166  int fd = 0;
167 #ifdef EPOLL_CLOEXEC
168  fd = epoll_create1(EPOLL_CLOEXEC);
169 
170  if (fd >= 0)
171  return fd;
172 
173  if (errno != EINVAL)
174  return -1;
175 
176 #endif
177  fd = epoll_create(1);
178  return set_cloexec_or_close(fd);
179 }
180 
181 static int create_tmpfile_cloexec(char* tmpname)
182 {
183  int fd = 0;
184 #ifdef USE_SHM
185  fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
186 #elif defined(UWAC_HAVE_MKOSTEMP)
187  fd = mkostemp(tmpname, O_CLOEXEC);
188 
189  if (fd >= 0)
190  unlink(tmpname);
191 
192 #else
193  fd = mkstemp(tmpname);
194 
195  if (fd >= 0)
196  {
197  fd = set_cloexec_or_close(fd);
198  unlink(tmpname);
199  }
200 
201 #endif
202  return fd;
203 }
204 
205 /*
206  * Create a new, unique, anonymous file of the given size, and
207  * return the file descriptor for it. The file descriptor is set
208  * CLOEXEC. The file is immediately suitable for mmap()'ing
209  * the given size at offset zero.
210  *
211  * The file should not have a permanent backing store like a disk,
212  * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
213  *
214  * The file name is deleted from the file system.
215  *
216  * The file is suitable for buffer sharing between processes by
217  * transmitting the file descriptor over Unix sockets using the
218  * SCM_RIGHTS methods.
219  *
220  * If the C library implements posix_fallocate(), it is used to
221  * guarantee that disk space is available for the file at the
222  * given size. If disk space is insufficient, errno is set to ENOSPC.
223  * If posix_fallocate() is not supported, program may receive
224  * SIGBUS on accessing mmap()'ed file contents instead.
225  */
226 int uwac_create_anonymous_file(off_t size)
227 {
228  static const char template[] = "/weston-shared-XXXXXX";
229  size_t length = 0;
230  char* name = NULL;
231  const char* path = NULL;
232  int fd = 0;
233  int ret = 0;
234  path = getenv("XDG_RUNTIME_DIR");
235 
236  if (!path)
237  {
238  errno = ENOENT;
239  return -1;
240  }
241 
242 #ifdef O_TMPFILE
243  fd = open(path, O_TMPFILE | O_RDWR | O_EXCL, 0600);
244 #else
245  /*
246  * Some platforms (e.g. FreeBSD) won't support O_TMPFILE and can't
247  * reasonably emulate it at first blush. Opt to make them rely on
248  * the create_tmpfile_cloexec() path instead.
249  */
250  fd = -1;
251 #endif
252 
253  if (fd < 0)
254  {
255  length = strlen(path) + sizeof(template);
256  name = xmalloc(length);
257 
258  if (!name)
259  return -1;
260 
261  (void)snprintf(name, length, "%s%s", path, template);
262  fd = create_tmpfile_cloexec(name);
263  free(name);
264  }
265 
266  if (fd < 0)
267  return -1;
268 
269 #ifdef UWAC_HAVE_POSIX_FALLOCATE
270  ret = posix_fallocate(fd, 0, size);
271 
272  if (ret != 0)
273  {
274  close(fd);
275  errno = ret;
276  return -1;
277  }
278 
279 #else
280  ret = ftruncate(fd, size);
281 
282  if (ret < 0)
283  {
284  close(fd);
285  return -1;
286  }
287 
288 #endif
289  return fd;
290 }