OSDN Git Service

1b36164396a087a47d43e00b00952cd4f5f25771
[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, 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 #include "../../libc/misc/internals/tempname.h"
37
38
39 /* Compatibility defines. */
40 #define __endmntent                    endmntent
41 #define __getmntent_r                  getmntent_r
42 #define __setmntent                    setmntent
43 #define __statfs                       statfs
44 #define __libc_close                   close
45 #ifdef __UCLIBC_HAS_LFS__
46 # define __libc_open                    open64
47 # define __fxstat64(vers, fd, buf)              fstat64(fd, buf)
48 #else
49 # define __libc_open                    open
50 # define __fxstat64(vers, fd, buf)              fstat(fd, buf)
51 # define stat64                                                 stat
52 #endif
53 #define __libc_write                   write
54
55
56 /* Information about the mount point.  */
57 struct mountpoint_info mountpoint attribute_hidden;
58
59 /* This is the default mount point.  */
60 static const char defaultmount[] = "/dev/shm";
61 /* This is the default directory.  */
62 static const char defaultdir[] = "/dev/shm/sem.";
63
64 /* Protect the `mountpoint' variable above.  */
65 pthread_once_t __namedsem_once attribute_hidden = PTHREAD_ONCE_INIT;
66
67
68 /* Determine where the shmfs is mounted (if at all).  */
69 void
70 attribute_hidden
71 __where_is_shmfs (void)
72 {
73   char buf[512];
74   struct statfs f;
75   struct mntent resmem;
76   struct mntent *mp;
77   FILE *fp;
78
79   /* The canonical place is /dev/shm.  This is at least what the
80      documentation tells everybody to do.  */
81   if (__statfs (defaultmount, &f) == 0 && f.f_type == SHMFS_SUPER_MAGIC)
82     {
83       /* It is in the normal place.  */
84       mountpoint.dir = (char *) defaultdir;
85       mountpoint.dirlen = sizeof (defaultdir) - 1;
86
87       return;
88     }
89
90   /* OK, do it the hard way.  Look through the /proc/mounts file and if
91      this does not exist through /etc/fstab to find the mount point.  */
92   fp = __setmntent ("/proc/mounts", "r");
93   if (unlikely (fp == NULL))
94     {
95       fp = __setmntent (_PATH_MNTTAB, "r");
96       if (unlikely (fp == NULL))
97         /* There is nothing we can do.  Blind guesses are not helpful.  */
98         return;
99     }
100
101   /* Now read the entries.  */
102   while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
103     /* The original name is "shm" but this got changed in early Linux
104        2.4.x to "tmpfs".  */
105     if (strcmp (mp->mnt_type, "tmpfs") == 0
106         || strcmp (mp->mnt_type, "shm") == 0)
107       {
108         /* Found it.  There might be more than one place where the
109            filesystem is mounted but one is enough for us.  */
110         size_t namelen;
111
112         /* First make sure this really is the correct entry.  At least
113            some versions of the kernel give wrong information because
114            of the implicit mount of the shmfs for SysV IPC.  */
115         if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
116           continue;
117
118         namelen = strlen (mp->mnt_dir);
119
120         if (namelen == 0)
121           /* Hum, maybe some crippled entry.  Keep on searching.  */
122           continue;
123
124         mountpoint.dir = (char *) malloc (namelen + 4 + 2);
125         if (mountpoint.dir != NULL)
126           {
127             char *cp = mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
128             if (cp[-1] != '/')
129               *cp++ = '/';
130             cp = stpcpy (cp, "sem.");
131             mountpoint.dirlen = cp - mountpoint.dir;
132           }
133
134         break;
135       }
136
137   /* Close the stream.  */
138   __endmntent (fp);
139 }
140
141
142 /* Comparison function for search of existing mapping.  */
143 int
144 attribute_hidden
145 __sem_search (const void *a, const void *b)
146 {
147   const struct inuse_sem *as = (const struct inuse_sem *) a;
148   const struct inuse_sem *bs = (const struct inuse_sem *) b;
149
150   if (as->ino != bs->ino)
151     /* Cannot return the difference the type is larger than int.  */
152     return as->ino < bs->ino ? -1 : (as->ino == bs->ino ? 0 : 1);
153
154   if (as->dev != bs->dev)
155     /* Cannot return the difference the type is larger than int.  */
156     return as->dev < bs->dev ? -1 : (as->dev == bs->dev ? 0 : 1);
157
158   return strcmp (as->name, bs->name);
159 }
160
161
162 /* The search tree for existing mappings.  */
163 void *__sem_mappings attribute_hidden;
164
165 /* Lock to protect the search tree.  */
166 int __sem_mappings_lock attribute_hidden = LLL_LOCK_INITIALIZER;
167
168
169 /* Search for existing mapping and if possible add the one provided.  */
170 static sem_t *
171 check_add_mapping (const char *name, size_t namelen, int fd, sem_t *existing)
172 {
173   sem_t *result = SEM_FAILED;
174
175   /* Get the information about the file.  */
176   struct stat64 st;
177   if (__fxstat64 (_STAT_VER, fd, &st) == 0)
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 }