OSDN Git Service

Merge remote branch 'origin/master' into nptl_merge
[uclinux-h8/uClibc.git] / libpthread / nptl / sem_open.c
1 /* Copyright (C) 2002, 2003 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 /* Information about the mount point.  */
51 struct mountpoint_info mountpoint attribute_hidden;
52
53 /* This is the default mount point.  */
54 static const char defaultmount[] = "/dev/shm";
55 /* This is the default directory.  */
56 static const char defaultdir[] = "/dev/shm/sem.";
57
58 /* Protect the `mountpoint' variable above.  */
59 pthread_once_t __namedsem_once attribute_hidden = PTHREAD_ONCE_INIT;
60
61
62 /* Determine where the shmfs is mounted (if at all).  */
63 void
64 attribute_hidden
65 __where_is_shmfs (void)
66 {
67   char buf[512];
68   struct statfs f;
69   struct mntent resmem;
70   struct mntent *mp;
71   FILE *fp;
72
73   /* The canonical place is /dev/shm.  This is at least what the
74      documentation tells everybody to do.  */
75   if (__statfs (defaultmount, &f) == 0 && f.f_type == SHMFS_SUPER_MAGIC)
76     {
77       /* It is in the normal place.  */
78       mountpoint.dir = (char *) defaultdir;
79       mountpoint.dirlen = sizeof (defaultdir) - 1;
80
81       return;
82     }
83
84   /* OK, do it the hard way.  Look through the /proc/mounts file and if
85      this does not exist through /etc/fstab to find the mount point.  */
86   fp = __setmntent ("/proc/mounts", "r");
87   if (__builtin_expect (fp == NULL, 0))
88     {
89       fp = __setmntent (_PATH_MNTTAB, "r");
90       if (__builtin_expect (fp == NULL, 0))
91         /* There is nothing we can do.  Blind guesses are not helpful.  */
92         return;
93     }
94
95   /* Now read the entries.  */
96   while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
97     /* The original name is "shm" but this got changed in early Linux
98        2.4.x to "tmpfs".  */
99     if (strcmp (mp->mnt_type, "tmpfs") == 0
100         || strcmp (mp->mnt_type, "shm") == 0)
101       {
102         /* Found it.  There might be more than one place where the
103            filesystem is mounted but one is enough for us.  */
104         size_t namelen;
105
106         /* First make sure this really is the correct entry.  At least
107            some versions of the kernel give wrong information because
108            of the implicit mount of the shmfs for SysV IPC.  */
109         if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
110           continue;
111
112         namelen = strlen (mp->mnt_dir);
113
114         if (namelen == 0)
115           /* Hum, maybe some crippled entry.  Keep on searching.  */
116           continue;
117
118         mountpoint.dir = (char *) malloc (namelen + 4 + 2);
119         if (mountpoint.dir != NULL)
120           {
121             char *cp = mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
122             if (cp[-1] != '/')
123               *cp++ = '/';
124             cp = stpcpy (cp, "sem.");
125             mountpoint.dirlen = cp - mountpoint.dir;
126           }
127
128         break;
129       }
130
131   /* Close the stream.  */
132   __endmntent (fp);
133 }
134
135
136 /* Comparison function for search of existing mapping.  */
137 int
138 attribute_hidden
139 __sem_search (const void *a, const void *b)
140 {
141   const struct inuse_sem *as = (const struct inuse_sem *) a;
142   const struct inuse_sem *bs = (const struct inuse_sem *) b;
143
144   if (as->ino != bs->ino)
145     /* Cannot return the difference the type is larger than int.  */
146     return as->ino < bs->ino ? -1 : (as->ino == bs->ino ? 0 : 1);
147
148   if (as->dev != bs->dev)
149     /* Cannot return the difference the type is larger than int.  */
150     return as->dev < bs->dev ? -1 : (as->dev == bs->dev ? 0 : 1);
151
152   return strcmp (as->name, bs->name);
153 }
154
155
156 /* The search tree for existing mappings.  */
157 void *__sem_mappings attribute_hidden;
158
159 /* Lock to protect the search tree.  */
160 lll_lock_t __sem_mappings_lock = LLL_LOCK_INITIALIZER;
161
162
163 /* Search for existing mapping and if possible add the one provided.  */
164 static sem_t *
165 check_add_mapping (const char *name, size_t namelen, int fd, sem_t *existing)
166 {
167   sem_t *result = SEM_FAILED;
168
169   /* Get the information about the file.  */
170 #ifdef __UCLIBC_HAS_LFS__
171   struct stat64 st;
172   if (__fxstat64 (_STAT_VER, fd, &st) == 0)
173 #else
174   struct stat st;
175   if (fstat (fd, &st) == 0)
176 #endif
177     {
178       /* Get the lock.  */
179       lll_lock (__sem_mappings_lock);
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);
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   __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       struct sem *iinitsem = (struct sem *) &initsem;
323       iinitsem->count = value;
324
325       /* Initialize the remaining bytes as well.  */
326       memset ((char *) &initsem + sizeof (struct sem), '\0',
327               sizeof (sem_t) - sizeof (struct sem));
328
329       tmpfname = (char *) alloca (mountpoint.dirlen + 6 + 1);
330       char *xxxxxx = mempcpy (tmpfname, mountpoint.dir, mountpoint.dirlen);
331       strcpy (xxxxxx, "XXXXXX");
332
333       fd = __gen_tempname (tmpfname, __GT_FILE, mode);
334       if (fd == -1)
335           return SEM_FAILED;
336
337       if (TEMP_FAILURE_RETRY (__libc_write (fd, &initsem, sizeof (sem_t)))
338           == sizeof (sem_t)
339           /* Map the sem_t structure from the file.  */
340           && (result = (sem_t *) mmap (NULL, sizeof (sem_t),
341                                        PROT_READ | PROT_WRITE, MAP_SHARED,
342                                        fd, 0)) != MAP_FAILED)
343         {
344           /* Create the file.  Don't overwrite an existing file.  */
345           if (link (tmpfname, finalname) != 0)
346             {
347               /* Undo the mapping.  */
348               (void) munmap (result, sizeof (sem_t));
349
350               /* Reinitialize 'result'.  */
351               result = SEM_FAILED;
352
353               /* This failed.  If O_EXCL is not set and the problem was
354                  that the file exists, try again.  */
355               if ((oflag & O_EXCL) == 0 && errno == EEXIST)
356                 {
357                   /* Remove the file.  */
358                   (void) unlink (tmpfname);
359
360                   /* Close the file.  */
361                   (void) __libc_close (fd);
362
363                   goto try_again;
364                 }
365             }
366           else
367             /* Insert the mapping into the search tree.  This also
368                determines whether another thread sneaked by and already
369                added such a mapping despite the fact that we created it.  */
370             result = check_add_mapping (name, namelen, fd, result);
371         }
372
373       /* Now remove the temporary name.  This should never fail.  If
374          it fails we leak a file name.  Better fix the kernel.  */
375       (void) unlink (tmpfname);
376     }
377
378   /* Map the mmap error to the error we need.  */
379   if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
380     result = SEM_FAILED;
381
382   /* We don't need the file descriptor anymore.  */
383   if (fd != -1)
384     {
385       /* Do not disturb errno.  */
386       INTERNAL_SYSCALL_DECL (err);
387       INTERNAL_SYSCALL (close, err, 1, fd);
388     }
389
390   return result;
391 }