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