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