(sem_open): Return SEM_FAILED if existing semaphore must be used and
[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;
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)
162     {
163       fd = __libc_open (finalname, oflag | O_NOFOLLOW);
164
165       if (fd == -1)
166         /* Return.  errno is already set.  */
167         return SEM_FAILED;
168
169       /* Map the sem_t structure from the file.  */
170       result = (sem_t *) mmap (NULL, sizeof (sem_t), PROT_READ | PROT_WRITE,
171                                MAP_SHARED, fd, 0);
172       if (MAP_FAILED != (void *) SEM_FAILED && result == MAP_FAILED)
173         result = SEM_FAILED;
174     }
175   else
176     {
177       /* We have to open a temporary file first since it must have the
178          correct form before we can start using it.  */
179       char *tmpfname;
180       mode_t mode;
181       unsigned int value;
182       va_list ap;
183
184       va_start (ap, oflag);
185
186       mode = va_arg (ap, mode_t);
187       value = va_arg (ap, unsigned int);
188
189       va_end (ap);
190
191       if (value > SEM_VALUE_MAX)
192         {
193           __set_errno (EINVAL);
194           return SEM_FAILED;
195         }
196
197       tmpfname = (char *) alloca (mountpoint.dirlen + 6 + 1);
198       strcpy (__mempcpy (tmpfname, mountpoint.dir, mountpoint.dirlen),
199               "XXXXXX");
200
201       fd = mkstemp (tmpfname);
202       if (fd == -1)
203         return SEM_FAILED;
204
205       /* Create the initial file content.  */
206       sem_t initsem;
207
208       struct sem *iinitsem = (struct sem *) &initsem;
209       iinitsem->count = value;
210
211       /* Initialize the remaining bytes as well.  */
212       memset ((char *) &initsem + sizeof (struct sem), '\0',
213               sizeof (sem_t) - sizeof (struct sem));
214
215       if (TEMP_FAILURE_RETRY (__libc_write (fd, &initsem, sizeof (sem_t)))
216           != sizeof (sem_t)
217           /* Adjust the permission.  */
218           || fchmod (fd, mode) != 0)
219         {
220         unlink_return:
221           unlink (tmpfname);
222           return SEM_FAILED;
223         }
224
225       /* Map the sem_t structure from the file.  */
226       result = (sem_t *) mmap (NULL, sizeof (sem_t), PROT_READ | PROT_WRITE,
227                                MAP_SHARED, fd, 0);
228       if (result == MAP_FAILED)
229         goto unlink_return;
230
231       /* Create or overwrite the file.  Depending on what is wanted we
232          use rename or link.  */
233       if ((oflag & O_EXCL) == 0)
234         {
235           /* An existing file gets overwritten.  */
236           if (rename (tmpfname, finalname) != 0)
237             {
238             unmap_unlink_return:
239               munmap (result, sizeof (sem_t));
240               goto unlink_return;
241             }
242         }
243       else
244         {
245           /* Don't overwrite an existing file.  */
246           if (link (tmpfname, finalname) != 0)
247             goto unmap_unlink_return;
248
249           /* This went well.  Now remove the temporary name.  This
250              should never fail.  If it fails we leak a file name.
251              Better fix the kernel.  */
252           (void) unlink (tmpfname);
253         }
254     }
255
256   /* We don't need the file descriptor anymore.  */
257   __libc_close (fd);
258
259   return result;
260 }