1 /* Copyright (C) 2002-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18 #include <fcntl.h>
19 #include <semaphore.h>
20 #include <stdarg.h>
21 #include <unistd.h>
22 #include <sys/mman.h>
23 #include "semaphoreP.h"
24 #include <shm-directory.h>
25 #include <sem_routines.h>
26 #include <futex-internal.h>
27 #include <libc-lock.h>
28
29 #if !PTHREAD_IN_LIBC
30 /* The private names are not exported from libc. */
31 # define __link link
32 # define __unlink unlink
33 #endif
34
35 sem_t *
__sem_open(const char * name,int oflag,...)36 __sem_open (const char *name, int oflag, ...)
37 {
38 int fd;
39 sem_t *result;
40
41 /* Check that shared futexes are supported. */
42 int err = futex_supports_pshared (PTHREAD_PROCESS_SHARED);
43 if (err != 0)
44 {
45 __set_errno (err);
46 return SEM_FAILED;
47 }
48
49 struct shmdir_name dirname;
50 if (__shm_get_name (&dirname, name, true) != 0)
51 {
52 __set_errno (EINVAL);
53 return SEM_FAILED;
54 }
55
56 /* Disable asynchronous cancellation. */
57 #ifdef __libc_ptf_call
58 int state;
59 __libc_ptf_call (__pthread_setcancelstate,
60 (PTHREAD_CANCEL_DISABLE, &state), 0);
61 #endif
62
63 /* If the semaphore object has to exist simply open it. */
64 if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
65 {
66 try_again:
67 fd = __open (dirname.name,
68 (oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);
69
70 if (fd == -1)
71 {
72 /* If we are supposed to create the file try this next. */
73 if ((oflag & O_CREAT) != 0 && errno == ENOENT)
74 goto try_create;
75
76 /* Return. errno is already set. */
77 }
78 else
79 /* Check whether we already have this semaphore mapped and
80 create one if necessary. */
81 result = __sem_check_add_mapping (name, fd, SEM_FAILED);
82 }
83 else
84 {
85 /* We have to open a temporary file first since it must have the
86 correct form before we can start using it. */
87 mode_t mode;
88 unsigned int value;
89 va_list ap;
90
91 try_create:
92 va_start (ap, oflag);
93
94 mode = va_arg (ap, mode_t);
95 value = va_arg (ap, unsigned int);
96
97 va_end (ap);
98
99 if (value > SEM_VALUE_MAX)
100 {
101 __set_errno (EINVAL);
102 result = SEM_FAILED;
103 goto out;
104 }
105
106 /* Create the initial file content. */
107 union
108 {
109 sem_t initsem;
110 struct new_sem newsem;
111 } sem;
112
113 __new_sem_open_init (&sem.newsem, value);
114
115 /* Initialize the remaining bytes as well. */
116 memset ((char *) &sem.initsem + sizeof (struct new_sem), '\0',
117 sizeof (sem_t) - sizeof (struct new_sem));
118
119 char tmpfname[] = SHMDIR "sem.XXXXXX";
120 int retries = 0;
121 #define NRETRIES 50
122 while (1)
123 {
124 /* We really want to use mktemp here. We cannot use mkstemp
125 since the file must be opened with a specific mode. The
126 mode cannot later be set since then we cannot apply the
127 file create mask. */
128 if (__mktemp (tmpfname) == NULL)
129 {
130 result = SEM_FAILED;
131 goto out;
132 }
133
134 /* Open the file. Make sure we do not overwrite anything. */
135 fd = __open (tmpfname, O_RDWR | O_CREAT | O_EXCL, mode);
136 if (fd == -1)
137 {
138 if (errno == EEXIST)
139 {
140 if (++retries < NRETRIES)
141 {
142 /* Restore the six placeholder bytes before the
143 null terminator before the next attempt. */
144 memcpy (tmpfname + sizeof (tmpfname) - 7, "XXXXXX", 6);
145 continue;
146 }
147
148 __set_errno (EAGAIN);
149 }
150
151 result = SEM_FAILED;
152 goto out;
153 }
154
155 /* We got a file. */
156 break;
157 }
158
159 if (TEMP_FAILURE_RETRY (write (fd, &sem.initsem, sizeof (sem_t)))
160 == sizeof (sem_t)
161 /* Map the sem_t structure from the file. */
162 && (result = (sem_t *) __mmap (NULL, sizeof (sem_t),
163 PROT_READ | PROT_WRITE, MAP_SHARED,
164 fd, 0)) != MAP_FAILED)
165 {
166 /* Create the file. Don't overwrite an existing file. */
167 if (__link (tmpfname, dirname.name) != 0)
168 {
169 /* Undo the mapping. */
170 __munmap (result, sizeof (sem_t));
171
172 /* Reinitialize 'result'. */
173 result = SEM_FAILED;
174
175 /* This failed. If O_EXCL is not set and the problem was
176 that the file exists, try again. */
177 if ((oflag & O_EXCL) == 0 && errno == EEXIST)
178 {
179 /* Remove the file. */
180 __unlink (tmpfname);
181
182 /* Close the file. */
183 __close (fd);
184
185 goto try_again;
186 }
187 }
188 else
189 /* Insert the mapping into the search tree. This also
190 determines whether another thread sneaked by and already
191 added such a mapping despite the fact that we created it. */
192 result = __sem_check_add_mapping (name, fd, result);
193 }
194
195 /* Now remove the temporary name. This should never fail. If
196 it fails we leak a file name. Better fix the kernel. */
197 __unlink (tmpfname);
198 }
199
200 /* Map the mmap error to the error we need. */
201 if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
202 result = SEM_FAILED;
203
204 /* We don't need the file descriptor anymore. */
205 if (fd != -1)
206 {
207 /* Do not disturb errno. */
208 int save = errno;
209 __close (fd);
210 errno = save;
211 }
212
213 out:
214 #ifdef __libc_ptf_call
215 __libc_ptf_call (__pthread_setcancelstate, (state, NULL), 0);
216 #endif
217
218 return result;
219 }
220 #if PTHREAD_IN_LIBC
221 versioned_symbol (libc, __sem_open, sem_open, GLIBC_2_34);
222 # if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_1_1, GLIBC_2_34)
223 compat_symbol (libpthread, __sem_open, sem_open, GLIBC_2_1_1);
224 # endif
225 #else /* !PTHREAD_IN_LIBC */
226 strong_alias (__sem_open, sem_open)
227 #endif
228