OSDN Git Service

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