(sem_open): Also close file descriptor before retry.
[kopensolaris-gnu/glibc.git] / 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 <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
37
38
39 /* Information about the mount point.  */
40 struct mountpoint_info mountpoint attribute_hidden;
41
42 /* This is the default mount point.  */
43 static const char defaultmount[] = "/dev/shm";
44 /* This is the default directory.  */
45 static const char defaultdir[] = "/dev/shm/sem.";
46
47 /* Protect the `mountpoint' variable above.  */
48 pthread_once_t __namedsem_once attribute_hidden = PTHREAD_ONCE_INIT;
49
50
51 /* Determine where the shmfs is mounted (if at all).  */
52 void
53 attribute_hidden
54 __where_is_shmfs (void)
55 {
56   char buf[512];
57   struct statfs f;
58   struct mntent resmem;
59   struct mntent *mp;
60   FILE *fp;
61
62   /* The canonical place is /dev/shm.  This is at least what the
63      documentation tells everybody to do.  */
64   if (__statfs (defaultmount, &f) == 0 && f.f_type == SHMFS_SUPER_MAGIC)
65     {
66       /* It is in the normal place.  */
67       mountpoint.dir = (char *) defaultdir;
68       mountpoint.dirlen = sizeof (defaultdir) - 1;
69
70       return;
71     }
72
73   /* OK, do it the hard way.  Look through the /proc/mounts file and if
74      this does not exist through /etc/fstab to find the mount point.  */
75   fp = __setmntent ("/proc/mounts", "r");
76   if (__builtin_expect (fp == NULL, 0))
77     {
78       fp = __setmntent (_PATH_MNTTAB, "r");
79       if (__builtin_expect (fp == NULL, 0))
80         /* There is nothing we can do.  Blind guesses are not helpful.  */
81         return;
82     }
83
84   /* Now read the entries.  */
85   while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
86     /* The original name is "shm" but this got changed in early Linux
87        2.4.x to "tmpfs".  */
88     if (strcmp (mp->mnt_type, "tmpfs") == 0
89         || strcmp (mp->mnt_type, "shm") == 0)
90       {
91         /* Found it.  There might be more than one place where the
92            filesystem is mounted but one is enough for us.  */
93         size_t namelen;
94
95         /* First make sure this really is the correct entry.  At least
96            some versions of the kernel give wrong information because
97            of the implicit mount of the shmfs for SysV IPC.  */
98         if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
99           continue;
100
101         namelen = strlen (mp->mnt_dir);
102
103         if (namelen == 0)
104           /* Hum, maybe some crippled entry.  Keep on searching.  */
105           continue;
106
107         mountpoint.dir = (char *) malloc (namelen + 4 + 2);
108         if (mountpoint.dir != NULL)
109           {
110             char *cp = __mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
111             if (cp[-1] != '/')
112               *cp++ = '/';
113             cp = stpcpy (cp, "sem.");
114             mountpoint.dirlen = cp - mountpoint.dir;
115           }
116
117         break;
118       }
119
120   /* Close the stream.  */
121   __endmntent (fp);
122 }
123
124
125 sem_t *
126 sem_open (const char *name, int oflag, ...)
127 {
128   char *finalname;
129   size_t namelen = SEM_FAILED;
130   sem_t *result;
131   int fd;
132
133   /* Determine where the shmfs is mounted.  */
134   INTUSE(__pthread_once) (&__namedsem_once, __where_is_shmfs);
135
136   /* If we don't know the mount points there is nothing we can do.  Ever.  */
137   if (mountpoint.dir == NULL)
138     {
139       __set_errno (ENOSYS);
140       return SEM_FAILED;
141     }
142
143   /* Construct the filename.  */
144   while (name[0] == '/')
145     ++name;
146
147   if (name[0] == '\0')
148     {
149       /* The name "/" is not supported.  */
150       __set_errno (EINVAL);
151       return SEM_FAILED;
152     }
153   namelen = strlen (name);
154
155   /* Create the name of the final file.  */
156   finalname = (char *) alloca (mountpoint.dirlen + namelen + 1);
157   __mempcpy (__mempcpy (finalname, mountpoint.dir, mountpoint.dirlen),
158              name, namelen + 1);
159
160   /* If the semaphore object has to exist simply open it.  */
161   if ((oflag & O_CREAT) == 0 || (oflag & O_EXCL) == 0)
162     {
163     try_again:
164       fd = __libc_open (finalname,
165                         (oflag & ~(O_CREAT|O_ACCMODE)) | O_NOFOLLOW | O_RDWR);
166
167       if (fd == -1)
168         {
169           /* If we are supposed to create the file try this next.  */
170           if ((oflag & O_CREAT) != 0)
171             goto try_create;
172
173           /* Return.  errno is already set.  */
174         }
175       else
176         /* Map the sem_t structure from the file.  */
177         result = (sem_t *) mmap (NULL, sizeof (sem_t), PROT_READ | PROT_WRITE,
178                                  MAP_SHARED, fd, 0);
179     }
180   else
181     {
182       /* We have to open a temporary file first since it must have the
183          correct form before we can start using it.  */
184       char *tmpfname;
185       mode_t mode;
186       unsigned int value;
187       va_list ap;
188
189     try_create:
190       va_start (ap, oflag);
191
192       mode = va_arg (ap, mode_t);
193       value = va_arg (ap, unsigned int);
194
195       va_end (ap);
196
197       if (value > SEM_VALUE_MAX)
198         {
199           __set_errno (EINVAL);
200           return SEM_FAILED;
201         }
202
203       tmpfname = (char *) alloca (mountpoint.dirlen + 6 + 1);
204       strcpy (__mempcpy (tmpfname, mountpoint.dir, mountpoint.dirlen),
205               "XXXXXX");
206
207       fd = mkstemp (tmpfname);
208       if (fd == -1)
209         return SEM_FAILED;
210
211       /* Create the initial file content.  */
212       sem_t initsem;
213
214       struct sem *iinitsem = (struct sem *) &initsem;
215       iinitsem->count = value;
216
217       /* Initialize the remaining bytes as well.  */
218       memset ((char *) &initsem + sizeof (struct sem), '\0',
219               sizeof (sem_t) - sizeof (struct sem));
220
221       if (TEMP_FAILURE_RETRY (__libc_write (fd, &initsem, sizeof (sem_t)))
222           == sizeof (sem_t)
223           /* Adjust the permission.  */
224           && fchmod (fd, mode) == 0
225           /* Map the sem_t structure from the file.  */
226           && (result = (sem_t *) mmap (NULL, sizeof (sem_t),
227                                        PROT_READ | PROT_WRITE, MAP_SHARED,
228                                        fd, 0)) != MAP_FAILED)
229         {
230           /* Create the file.  Don't overwrite an existing file.  */
231           if (link (tmpfname, finalname) != 0)
232             {
233               /* Undo the mapping.  */
234               (void) munmap (result, sizeof (sem_t));
235
236               /* Reinitialize 'result'.  */
237               result = SEM_FAILED;
238
239               /* This failed.  If O_EXCL is not set and the problem was
240                  that the file exists, try again.  */
241               if ((oflag & O_EXCL) == 0 && errno == EEXIST)
242                 {
243                   /* Remove the file.  */
244                   (void) unlink (tmpfname);
245
246                   /* Close the file.  */
247                   (void) __libc_close (fd);
248
249                   goto try_again;
250                 }
251             }
252         }
253
254       /* Now remove the temporary name.  This should never fail.  If
255          it fails we leak a file name.  Better fix the kernel.  */
256       (void) unlink (tmpfname);
257     }
258
259   /* Map the mmap error to the error we need.  */
260   if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
261     result = SEM_FAILED;
262
263   /* We don't need the file descriptor anymore.  */
264   (void) __libc_close (fd);
265
266   return result;
267 }