]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/unix/opendir.c
Use <> for include of kernel-features.h.
[thirdparty/glibc.git] / sysdeps / unix / opendir.c
CommitLineData
28377d1b 1/* Copyright (C) 1991-1996,98,2000-2003,2005,2007,2009,2011
858ee15d 2 Free Software Foundation, Inc.
01c901a5
UD
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
01c901a5
UD
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
41bdb6e2 13 Lesser General Public License for more details.
01c901a5 14
41bdb6e2
AJ
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. */
28f540f4 19
c55fbd1e 20#include <assert.h>
28f540f4
RM
21#include <errno.h>
22#include <limits.h>
23#include <stddef.h>
24#include <stdlib.h>
25#include <dirent.h>
26#include <fcntl.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <unistd.h>
30#include <stdio.h>
39358e8b 31#include <string.h>
28f540f4 32
92777700 33#include <dirstream.h>
73299943 34#include <not-cancel.h>
cbf0489b 35#include <kernel-features.h>
73299943 36
28f540f4 37
8b7fb588
UD
38/* opendir() must not accidentally open something other than a directory.
39 Some OS's have kernel support for that, some don't. In the worst
40 case we have to stat() before the open() AND fstat() after.
41
42 We have to test at runtime for kernel support since libc may have
43 been compiled with different headers to the kernel it's running on.
44 This test can't be done reliably in the general case. We'll use
45 /dev/null, which if it's not a device lots of stuff will break, as
46 a guinea pig. It may be missing in chroot environments, so we
47 make sure to fail safe. */
413bb8ca 48#ifdef O_DIRECTORY
675e4015
UD
49# ifdef O_DIRECTORY_WORKS
50# define o_directory_works 1
51# define tryopen_o_directory() while (1) /* This must not be called. */
52# else
8b7fb588 53static int o_directory_works;
413bb8ca
UD
54
55static void
8b7fb588
UD
56tryopen_o_directory (void)
57{
58 int serrno = errno;
73299943 59 int x = open_not_cancel_2 ("/dev/null", O_RDONLY|O_NDELAY|O_DIRECTORY);
8b7fb588
UD
60
61 if (x >= 0)
62 {
73299943 63 close_not_cancel_no_status (x);
8b7fb588
UD
64 o_directory_works = -1;
65 }
66 else if (errno != ENOTDIR)
67 o_directory_works = -1;
68 else
69 o_directory_works = 1;
70
71 __set_errno (serrno);
72}
675e4015 73# endif
413bb8ca
UD
74# define EXTRA_FLAGS O_DIRECTORY
75#else
76# define EXTRA_FLAGS 0
9ffbb612
UD
77#endif
78
79
28f540f4 80DIR *
c55fbd1e
UD
81internal_function
82__opendirat (int dfd, const char *name)
28f540f4 83{
8edf6e0d 84 struct stat64 statbuf;
62f63c47 85 struct stat64 *statp = NULL;
28f540f4 86
726b7b0f 87 if (__builtin_expect (name[0], '\1') == '\0')
28f540f4
RM
88 {
89 /* POSIX.1-1990 says an empty name gets ENOENT;
90 but `open' might like it fine. */
c4029823 91 __set_errno (ENOENT);
28f540f4
RM
92 return NULL;
93 }
94
413bb8ca
UD
95#ifdef O_DIRECTORY
96 /* Test whether O_DIRECTORY works. */
8b7fb588 97 if (o_directory_works == 0)
413bb8ca
UD
98 tryopen_o_directory ();
99
100 /* We can skip the expensive `stat' call if O_DIRECTORY works. */
8b7fb588 101 if (o_directory_works < 0)
413bb8ca 102#endif
99e46354 103 {
413bb8ca
UD
104 /* We first have to check whether the name is for a directory. We
105 cannot do this after the open() call since the open/close operation
106 performed on, say, a tape device might have undesirable effects. */
726b7b0f 107 if (__builtin_expect (__xstat64 (_STAT_VER, name, &statbuf), 0) < 0)
413bb8ca 108 return NULL;
726b7b0f 109 if (__builtin_expect (! S_ISDIR (statbuf.st_mode), 0))
413bb8ca
UD
110 {
111 __set_errno (ENOTDIR);
112 return NULL;
113 }
99e46354
UD
114 }
115
cbf0489b
UD
116 int flags = O_RDONLY|O_NDELAY|EXTRA_FLAGS|O_LARGEFILE;
117#ifdef O_CLOEXEC
118 flags |= O_CLOEXEC;
119#endif
c55fbd1e
UD
120 int fd;
121#ifdef IS_IN_rtld
122 assert (dfd == AT_FDCWD);
123 fd = open_not_cancel_2 (name, flags);
124#else
125 fd = openat_not_cancel_3 (dfd, name, flags);
126#endif
726b7b0f 127 if (__builtin_expect (fd, 0) < 0)
4bd8be48
UD
128 return NULL;
129
256ba888
UD
130#ifdef O_DIRECTORY
131 if (o_directory_works <= 0)
132#endif
4bd8be48 133 {
62f63c47
UD
134 /* Now make sure this really is a directory and nothing changed since
135 the `stat' call. */
136 if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &statbuf), 0) < 0)
137 goto lose;
256ba888
UD
138 if (__builtin_expect (! S_ISDIR (statbuf.st_mode), 0))
139 {
1812d50b
UD
140 __set_errno (ENOTDIR);
141 lose:
142 close_not_cancel_no_status (fd);
143 return NULL;
256ba888 144 }
62f63c47 145 statp = &statbuf;
4bd8be48 146 }
28f540f4 147
28377d1b 148 return __alloc_dir (fd, true, 0, statp);
1812d50b 149}
c55fbd1e
UD
150
151
152/* Open a directory stream on NAME. */
153DIR *
154__opendir (const char *name)
155{
156 return __opendirat (AT_FDCWD, name);
157}
1812d50b
UD
158weak_alias (__opendir, opendir)
159
160
cbf0489b
UD
161#ifdef __ASSUME_O_CLOEXEC
162# define check_have_o_cloexec(fd) 1
163#else
164static int
165check_have_o_cloexec (int fd)
166{
167 if (__have_o_cloexec == 0)
168 __have_o_cloexec = (__fcntl (fd, F_GETFD, 0) & FD_CLOEXEC) == 0 ? -1 : 1;
169 return __have_o_cloexec > 0;
170}
171#endif
172
173
1812d50b
UD
174DIR *
175internal_function
28377d1b 176__alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp)
1812d50b 177{
cbf0489b
UD
178 /* We always have to set the close-on-exit flag if the user provided
179 the file descriptor. Otherwise only if we have no working
180 O_CLOEXEC support. */
181#ifdef O_CLOEXEC
28377d1b
UD
182 if ((! close_fd && (flags & O_CLOEXEC) == 0)
183 || ! check_have_o_cloexec (fd))
cbf0489b
UD
184#endif
185 {
186 if (__builtin_expect (__fcntl (fd, F_SETFD, FD_CLOEXEC), 0) < 0)
187 goto lose;
188 }
28f540f4 189
62f63c47
UD
190 const size_t default_allocation = (4 * BUFSIZ < sizeof (struct dirent64)
191 ? sizeof (struct dirent64) : 4 * BUFSIZ);
192 const size_t small_allocation = (BUFSIZ < sizeof (struct dirent64)
193 ? sizeof (struct dirent64) : BUFSIZ);
194 size_t allocation = default_allocation;
28f540f4 195#ifdef _STATBUF_ST_BLKSIZE
62f63c47 196 if (statp != NULL && default_allocation < statp->st_blksize)
1812d50b 197 allocation = statp->st_blksize;
28f540f4 198#endif
9eecb5e8 199
fa39685d 200 DIR *dirp = (DIR *) malloc (sizeof (DIR) + allocation);
9eecb5e8 201 if (dirp == NULL)
28f540f4 202 {
62f63c47
UD
203 allocation = small_allocation;
204 dirp = (DIR *) malloc (sizeof (DIR) + allocation);
205
221e5230 206 if (dirp == NULL)
221e5230
UD
207 lose:
208 {
209 if (close_fd)
210 {
211 int save_errno = errno;
212 close_not_cancel_no_status (fd);
213 __set_errno (save_errno);
214 }
215 return NULL;
18b8e054 216 }
28f540f4 217 }
c1509239 218
c8ccd8e3 219 dirp->fd = fd;
858ee15d 220#ifndef NOT_IN_libc
0d204b0a 221 __libc_lock_init (dirp->lock);
858ee15d 222#endif
c8ccd8e3 223 dirp->allocation = allocation;
fa39685d
UD
224 dirp->size = 0;
225 dirp->offset = 0;
226 dirp->filepos = 0;
c1509239 227
28f540f4
RM
228 return dirp;
229}