]> git.ipfire.org Git - thirdparty/glibc.git/blob - nptl/sem_open.c
Hide reference to mktemp in libpthread
[thirdparty/glibc.git] / nptl / sem_open.c
1 /* Copyright (C) 2002-2013 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <mntent.h>
22 #include <paths.h>
23 #include <pthread.h>
24 #include <search.h>
25 #include <semaphore.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #include <sys/statfs.h>
34 #include <linux_fsinfo.h>
35 #include "semaphoreP.h"
36
37
38
39 /* Information about the mount point. */
40 struct mountpoint_info mountpoint attribute_hidden;
41
42 /* This is the default mount point. */
43 static const char defaultmount[] = "/dev/shm";
44 /* This is the default directory. */
45 static const char defaultdir[] = "/dev/shm/sem.";
46
47 /* Protect the `mountpoint' variable above. */
48 pthread_once_t __namedsem_once attribute_hidden = PTHREAD_ONCE_INIT;
49
50
51 /* Determine where the shmfs is mounted (if at all). */
52 void
53 attribute_hidden
54 __where_is_shmfs (void)
55 {
56 char buf[512];
57 struct statfs f;
58 struct mntent resmem;
59 struct mntent *mp;
60 FILE *fp;
61
62 /* The canonical place is /dev/shm. This is at least what the
63 documentation tells everybody to do. */
64 if (__statfs (defaultmount, &f) == 0 && f.f_type == SHMFS_SUPER_MAGIC)
65 {
66 /* It is in the normal place. */
67 mountpoint.dir = (char *) defaultdir;
68 mountpoint.dirlen = sizeof (defaultdir) - 1;
69
70 return;
71 }
72
73 /* OK, do it the hard way. Look through the /proc/mounts file and if
74 this does not exist through /etc/fstab to find the mount point. */
75 fp = __setmntent ("/proc/mounts", "r");
76 if (__builtin_expect (fp == NULL, 0))
77 {
78 fp = __setmntent (_PATH_MNTTAB, "r");
79 if (__builtin_expect (fp == NULL, 0))
80 /* There is nothing we can do. Blind guesses are not helpful. */
81 return;
82 }
83
84 /* Now read the entries. */
85 while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
86 /* The original name is "shm" but this got changed in early Linux
87 2.4.x to "tmpfs". */
88 if (strcmp (mp->mnt_type, "tmpfs") == 0
89 || strcmp (mp->mnt_type, "shm") == 0)
90 {
91 /* Found it. There might be more than one place where the
92 filesystem is mounted but one is enough for us. */
93 size_t namelen;
94
95 /* First make sure this really is the correct entry. At least
96 some versions of the kernel give wrong information because
97 of the implicit mount of the shmfs for SysV IPC. */
98 if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
99 continue;
100
101 namelen = strlen (mp->mnt_dir);
102
103 if (namelen == 0)
104 /* Hum, maybe some crippled entry. Keep on searching. */
105 continue;
106
107 mountpoint.dir = (char *) malloc (namelen + 4 + 2);
108 if (mountpoint.dir != NULL)
109 {
110 char *cp = __mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
111 if (cp[-1] != '/')
112 *cp++ = '/';
113 cp = stpcpy (cp, "sem.");
114 mountpoint.dirlen = cp - mountpoint.dir;
115 }
116
117 break;
118 }
119
120 /* Close the stream. */
121 __endmntent (fp);
122 }
123
124
125 /* Comparison function for search of existing mapping. */
126 int
127 attribute_hidden
128 __sem_search (const void *a, const void *b)
129 {
130 const struct inuse_sem *as = (const struct inuse_sem *) a;
131 const struct inuse_sem *bs = (const struct inuse_sem *) b;
132
133 if (as->ino != bs->ino)
134 /* Cannot return the difference the type is larger than int. */
135 return as->ino < bs->ino ? -1 : (as->ino == bs->ino ? 0 : 1);
136
137 if (as->dev != bs->dev)
138 /* Cannot return the difference the type is larger than int. */
139 return as->dev < bs->dev ? -1 : (as->dev == bs->dev ? 0 : 1);
140
141 return strcmp (as->name, bs->name);
142 }
143
144
145 /* The search tree for existing mappings. */
146 void *__sem_mappings attribute_hidden;
147
148 /* Lock to protect the search tree. */
149 int __sem_mappings_lock attribute_hidden = LLL_LOCK_INITIALIZER;
150
151
152 /* Search for existing mapping and if possible add the one provided. */
153 static sem_t *
154 check_add_mapping (const char *name, size_t namelen, int fd, sem_t *existing)
155 {
156 sem_t *result = SEM_FAILED;
157
158 /* Get the information about the file. */
159 struct stat64 st;
160 if (__fxstat64 (_STAT_VER, fd, &st) == 0)
161 {
162 /* Get the lock. */
163 lll_lock (__sem_mappings_lock, LLL_PRIVATE);
164
165 /* Search for an existing mapping given the information we have. */
166 struct inuse_sem *fake;
167 fake = (struct inuse_sem *) alloca (sizeof (*fake) + namelen);
168 memcpy (fake->name, name, namelen);
169 fake->dev = st.st_dev;
170 fake->ino = st.st_ino;
171
172 struct inuse_sem **foundp = tfind (fake, &__sem_mappings, __sem_search);
173 if (foundp != NULL)
174 {
175 /* There is already a mapping. Use it. */
176 result = (*foundp)->sem;
177 ++(*foundp)->refcnt;
178 }
179 else
180 {
181 /* We haven't found a mapping. Install ione. */
182 struct inuse_sem *newp;
183
184 newp = (struct inuse_sem *) malloc (sizeof (*newp) + namelen);
185 if (newp != NULL)
186 {
187 /* If the caller hasn't provided any map it now. */
188 if (existing == SEM_FAILED)
189 existing = (sem_t *) mmap (NULL, sizeof (sem_t),
190 PROT_READ | PROT_WRITE, MAP_SHARED,
191 fd, 0);
192
193 newp->dev = st.st_dev;
194 newp->ino = st.st_ino;
195 newp->refcnt = 1;
196 newp->sem = existing;
197 memcpy (newp->name, name, namelen);
198
199 /* Insert the new value. */
200 if (existing != MAP_FAILED
201 && tsearch (newp, &__sem_mappings, __sem_search) != NULL)
202 /* Successful. */
203 result = existing;
204 else
205 /* Something went wrong while inserting the new
206 value. We fail completely. */
207 free (newp);
208 }
209 }
210
211 /* Release the lock. */
212 lll_unlock (__sem_mappings_lock, LLL_PRIVATE);
213 }
214
215 if (result != existing && existing != SEM_FAILED && existing != MAP_FAILED)
216 {
217 /* Do not disturb errno. */
218 INTERNAL_SYSCALL_DECL (err);
219 INTERNAL_SYSCALL (munmap, err, 2, existing, sizeof (sem_t));
220 }
221
222 return result;
223 }
224
225
226 sem_t *
227 sem_open (const char *name, int oflag, ...)
228 {
229 char *finalname;
230 sem_t *result = SEM_FAILED;
231 int fd;
232
233 /* Determine where the shmfs is mounted. */
234 __pthread_once (&__namedsem_once, __where_is_shmfs);
235
236 /* If we don't know the mount points there is nothing we can do. Ever. */
237 if (mountpoint.dir == NULL)
238 {
239 __set_errno (ENOSYS);
240 return SEM_FAILED;
241 }
242
243 /* Construct the filename. */
244 while (name[0] == '/')
245 ++name;
246
247 if (name[0] == '\0')
248 {
249 /* The name "/" is not supported. */
250 __set_errno (EINVAL);
251 return SEM_FAILED;
252 }
253 size_t namelen = strlen (name) + 1;
254
255 /* Create the name of the final file. */
256 finalname = (char *) alloca (mountpoint.dirlen + namelen);
257 __mempcpy (__mempcpy (finalname, mountpoint.dir, mountpoint.dirlen),
258 name, namelen);
259
260 /* If the semaphore object has to exist simply open it. */
261 if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
262 {
263 try_again:
264 fd = __libc_open (finalname,
265 (oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);
266
267 if (fd == -1)
268 {
269 /* If we are supposed to create the file try this next. */
270 if ((oflag & O_CREAT) != 0 && errno == ENOENT)
271 goto try_create;
272
273 /* Return. errno is already set. */
274 }
275 else
276 /* Check whether we already have this semaphore mapped and
277 create one if necessary. */
278 result = check_add_mapping (name, namelen, fd, SEM_FAILED);
279 }
280 else
281 {
282 /* We have to open a temporary file first since it must have the
283 correct form before we can start using it. */
284 char *tmpfname;
285 mode_t mode;
286 unsigned int value;
287 va_list ap;
288
289 try_create:
290 va_start (ap, oflag);
291
292 mode = va_arg (ap, mode_t);
293 value = va_arg (ap, unsigned int);
294
295 va_end (ap);
296
297 if (value > SEM_VALUE_MAX)
298 {
299 __set_errno (EINVAL);
300 return SEM_FAILED;
301 }
302
303 /* Create the initial file content. */
304 union
305 {
306 sem_t initsem;
307 struct new_sem newsem;
308 } sem;
309
310 sem.newsem.value = value;
311 sem.newsem.private = 0;
312 sem.newsem.nwaiters = 0;
313
314 /* Initialize the remaining bytes as well. */
315 memset ((char *) &sem.initsem + sizeof (struct new_sem), '\0',
316 sizeof (sem_t) - sizeof (struct new_sem));
317
318 tmpfname = (char *) alloca (mountpoint.dirlen + 6 + 1);
319 char *xxxxxx = __mempcpy (tmpfname, mountpoint.dir, mountpoint.dirlen);
320
321 int retries = 0;
322 #define NRETRIES 50
323 while (1)
324 {
325 /* Add the suffix for mktemp. */
326 strcpy (xxxxxx, "XXXXXX");
327
328 /* We really want to use mktemp here. We cannot use mkstemp
329 since the file must be opened with a specific mode. The
330 mode cannot later be set since then we cannot apply the
331 file create mask. */
332 if (__mktemp (tmpfname) == NULL)
333 return SEM_FAILED;
334
335 /* Open the file. Make sure we do not overwrite anything. */
336 fd = __libc_open (tmpfname, O_RDWR | O_CREAT | O_EXCL, mode);
337 if (fd == -1)
338 {
339 if (errno == EEXIST)
340 {
341 if (++retries < NRETRIES)
342 continue;
343
344 __set_errno (EAGAIN);
345 }
346
347 return SEM_FAILED;
348 }
349
350 /* We got a file. */
351 break;
352 }
353
354 if (TEMP_FAILURE_RETRY (__libc_write (fd, &sem.initsem, sizeof (sem_t)))
355 == sizeof (sem_t)
356 /* Map the sem_t structure from the file. */
357 && (result = (sem_t *) mmap (NULL, sizeof (sem_t),
358 PROT_READ | PROT_WRITE, MAP_SHARED,
359 fd, 0)) != MAP_FAILED)
360 {
361 /* Create the file. Don't overwrite an existing file. */
362 if (link (tmpfname, finalname) != 0)
363 {
364 /* Undo the mapping. */
365 (void) munmap (result, sizeof (sem_t));
366
367 /* Reinitialize 'result'. */
368 result = SEM_FAILED;
369
370 /* This failed. If O_EXCL is not set and the problem was
371 that the file exists, try again. */
372 if ((oflag & O_EXCL) == 0 && errno == EEXIST)
373 {
374 /* Remove the file. */
375 (void) unlink (tmpfname);
376
377 /* Close the file. */
378 (void) __libc_close (fd);
379
380 goto try_again;
381 }
382 }
383 else
384 /* Insert the mapping into the search tree. This also
385 determines whether another thread sneaked by and already
386 added such a mapping despite the fact that we created it. */
387 result = check_add_mapping (name, namelen, fd, result);
388 }
389
390 /* Now remove the temporary name. This should never fail. If
391 it fails we leak a file name. Better fix the kernel. */
392 (void) unlink (tmpfname);
393 }
394
395 /* Map the mmap error to the error we need. */
396 if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
397 result = SEM_FAILED;
398
399 /* We don't need the file descriptor anymore. */
400 if (fd != -1)
401 {
402 /* Do not disturb errno. */
403 INTERNAL_SYSCALL_DECL (err);
404 INTERNAL_SYSCALL (close, err, 1, fd);
405 }
406
407 return result;
408 }