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