(shm_open): Use O_CLOEXEC is available.
[platform/upstream/glibc.git] / sysdeps / unix / sysv / linux / shm_open.c
1 /* Copyright (C) 2000-2004,2006,2007 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <mntent.h>
22 #include <paths.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/mman.h>
28 #include <sys/statfs.h>
29 #include <bits/libc-lock.h>
30 #include "linux_fsinfo.h"
31
32 #include <kernel-features.h>
33
34
35 /* Mount point of the shared memory filesystem.  */
36 static struct
37 {
38   char *dir;
39   size_t dirlen;
40 } mountpoint;
41
42 /* This is the default directory.  */
43 static const char defaultdir[] = "/dev/shm/";
44
45 /* Protect the `mountpoint' variable above.  */
46 __libc_once_define (static, once);
47
48
49 #if defined O_CLOEXEC && !defined __ASSUME_O_CLOEXEC
50 static bool have_o_cloexec;
51 #endif
52
53
54 /* Determine where the shmfs is mounted (if at all).  */
55 static void
56 where_is_shmfs (void)
57 {
58   char buf[512];
59   struct statfs f;
60   struct mntent resmem;
61   struct mntent *mp;
62   FILE *fp;
63
64   /* The canonical place is /dev/shm.  This is at least what the
65      documentation tells everybody to do.  */
66   if (__statfs (defaultdir, &f) == 0 && f.f_type == SHMFS_SUPER_MAGIC)
67     {
68       /* It is in the normal place.  */
69       mountpoint.dir = (char *) defaultdir;
70       mountpoint.dirlen = sizeof (defaultdir) - 1;
71
72       return;
73     }
74
75   /* OK, do it the hard way.  Look through the /proc/mounts file and if
76      this does not exist through /etc/fstab to find the mount point.  */
77   fp = __setmntent ("/proc/mounts", "r");
78   if (__builtin_expect (fp == NULL, 0))
79     {
80       fp = __setmntent (_PATH_MNTTAB, "r");
81       if (__builtin_expect (fp == NULL, 0))
82         /* There is nothing we can do.  Blind guesses are not helpful.  */
83         return;
84     }
85
86   /* Now read the entries.  */
87   while ((mp = __getmntent_r (fp, &resmem, buf, sizeof buf)) != NULL)
88     /* The original name is "shm" but this got changed in early Linux
89        2.4.x to "tmpfs".  */
90     if (strcmp (mp->mnt_type, "tmpfs") == 0
91 #ifndef __ASSUME_TMPFS_NAME
92         || strcmp (mp->mnt_type, "shm") == 0
93 #endif
94         )
95       {
96         /* Found it.  There might be more than one place where the
97            filesystem is mounted but one is enough for us.  */
98         size_t namelen;
99
100         /* First make sure this really is the correct entry.  At least
101            some versions of the kernel give wrong information because
102            of the implicit mount of the shmfs for SysV IPC.  */
103         if (__statfs (mp->mnt_dir, &f) != 0 || f.f_type != SHMFS_SUPER_MAGIC)
104           continue;
105
106         namelen = strlen (mp->mnt_dir);
107
108         if (namelen == 0)
109           /* Hum, maybe some crippled entry.  Keep on searching.  */
110           continue;
111
112         mountpoint.dir = (char *) malloc (namelen + 2);
113         if (mountpoint.dir != NULL)
114           {
115             char *cp = __mempcpy (mountpoint.dir, mp->mnt_dir, namelen);
116             if (cp[-1] != '/')
117               *cp++ = '/';
118             *cp = '\0';
119             mountpoint.dirlen = cp - mountpoint.dir;
120           }
121
122         break;
123       }
124
125   /* Close the stream.  */
126   __endmntent (fp);
127 }
128
129
130 /* Open shared memory object.  This implementation assumes the shmfs
131    implementation introduced in the late 2.3.x kernel series to be
132    available.  Normally the filesystem will be mounted at /dev/shm but
133    we fall back on searching for the actual mount point should opening
134    such a file fail.  */
135 int
136 shm_open (const char *name, int oflag, mode_t mode)
137 {
138   size_t namelen;
139   char *fname;
140   int fd;
141
142   /* Determine where the shmfs is mounted.  */
143   __libc_once (once, where_is_shmfs);
144
145   /* If we don't know the mount points there is nothing we can do.  Ever.  */
146   if (mountpoint.dir == NULL)
147     {
148       __set_errno (ENOSYS);
149       return -1;
150     }
151
152   /* Construct the filename.  */
153   while (name[0] == '/')
154     ++name;
155
156   if (name[0] == '\0')
157     {
158       /* The name "/" is not supported.  */
159       __set_errno (EINVAL);
160       return -1;
161     }
162
163   namelen = strlen (name);
164   fname = (char *) alloca (mountpoint.dirlen + namelen + 1);
165   __mempcpy (__mempcpy (fname, mountpoint.dir, mountpoint.dirlen),
166              name, namelen + 1);
167
168 #ifdef O_CLOEXEC
169   oflag |= O_CLOEXEC;
170 #endif
171
172   /* And get the file descriptor.
173      XXX Maybe we should test each descriptor whether it really is for a
174      file on the shmfs.  If this is what should be done the whole function
175      should be revamped since we can determine whether shmfs is available
176      while trying to open the file, all in one turn.  */
177   fd = open (fname, oflag | O_NOFOLLOW, mode);
178   if (fd != -1)
179     {
180 #if !defined O_CLOEXEC || !defined __ASSUME_O_CLOEXEC
181 # ifdef O_CLOEXEC
182       if (have_o_cloexec <= 0)
183 # endif
184         {
185           /* We got a descriptor.  Now set the FD_CLOEXEC bit.  */
186           int flags = fcntl (fd, F_GETFD, 0);
187
188           if (__builtin_expect (flags, 0) >= 0)
189             {
190 # ifndef O_CLOEXEC
191               if (have_o_cloexec == 0)
192                 have_o_cloexec = (flags & FD_CLOEXEC) == 0 ? -1 : 1;
193               if (have_o_cloexec < 0)
194 # endif
195                 {
196                   flags |= FD_CLOEXEC;
197                   flags = fcntl (fd, F_SETFD, flags);
198                 }
199             }
200
201           if (flags == -1)
202             {
203               /* Something went wrong.  We cannot return the descriptor.  */
204               int save_errno = errno;
205               close (fd);
206               fd = -1;
207               __set_errno (save_errno);
208             }
209         }
210 #endif
211     }
212   else if (__builtin_expect (errno == EISDIR, 0))
213     /* It might be better to fold this error with EINVAL since
214        directory names are just another example for unsuitable shared
215        object names and the standard does not mention EISDIR.  */
216     __set_errno (EINVAL);
217
218   return fd;
219 }
220
221
222 /* Unlink a shared memory object.  */
223 int
224 shm_unlink (const char *name)
225 {
226   size_t namelen;
227   char *fname;
228
229   /* Determine where the shmfs is mounted.  */
230   __libc_once (once, where_is_shmfs);
231
232   if (mountpoint.dir == NULL)
233     {
234       /* We cannot find the shmfs.  If `name' is really a shared
235          memory object it must have been created by another process
236          and we have no idea where that process found the mountpoint.  */
237       __set_errno (ENOENT);
238       return -1;
239     }
240
241   /* Construct the filename.  */
242   while (name[0] == '/')
243     ++name;
244
245   if (name[0] == '\0')
246     {
247       /* The name "/" is not supported.  */
248       __set_errno (ENOENT);
249       return -1;
250     }
251
252   namelen = strlen (name);
253   fname = (char *) alloca (mountpoint.dirlen + namelen + 1);
254   __mempcpy (__mempcpy (fname, mountpoint.dir, mountpoint.dirlen),
255              name, namelen + 1);
256
257   /* And remove the file.  */
258   int ret = unlink (fname);
259   if (ret < 0 && errno == EPERM)
260     __set_errno (EACCES);
261   return ret;
262 }
263
264
265 /* Make sure the table is freed if we want to free everything before
266    exiting.  */
267 libc_freeres_fn (freeit)
268 {
269   if (mountpoint.dir != defaultdir)
270     free (mountpoint.dir);
271 }