]>
Commit | Line | Data |
---|---|---|
04277e02 | 1 | /* Copyright (C) 1991-2019 Free Software Foundation, Inc. |
01c901a5 UD |
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 | |
41bdb6e2 AJ |
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. | |
01c901a5 UD |
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 | |
41bdb6e2 | 12 | Lesser General Public License for more details. |
01c901a5 | 13 | |
41bdb6e2 | 14 | You should have received a copy of the GNU Lesser General Public |
59ba27a6 | 15 | License along with the GNU C Library; if not, see |
5a82c748 | 16 | <https://www.gnu.org/licenses/>. */ |
28f540f4 | 17 | |
28f540f4 RM |
18 | #include <dirent.h> |
19 | #include <fcntl.h> | |
8e4754ed AZ |
20 | #include <errno.h> |
21 | #include <stdio.h> /* For BUFSIZ. */ | |
22 | #include <sys/param.h> /* For MIN and MAX. */ | |
23 | ||
73299943 UD |
24 | #include <not-cancel.h> |
25 | ||
172a631a FW |
26 | /* The st_blksize value of the directory is used as a hint for the |
27 | size of the buffer which receives struct dirent values from the | |
28 | kernel. st_blksize is limited to MAX_DIR_BUFFER_SIZE, in case the | |
29 | file system provides a bogus value. */ | |
30 | #define MAX_DIR_BUFFER_SIZE 1048576U | |
28f540f4 | 31 | |
cef9b653 | 32 | enum { |
8e4754ed | 33 | opendir_oflags = O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC |
cef9b653 | 34 | }; |
9ffbb612 | 35 | |
46f894d8 RM |
36 | static bool |
37 | invalid_name (const char *name) | |
28f540f4 | 38 | { |
46f894d8 | 39 | if (__glibc_unlikely (name[0] == '\0')) |
28f540f4 RM |
40 | { |
41 | /* POSIX.1-1990 says an empty name gets ENOENT; | |
42 | but `open' might like it fine. */ | |
c4029823 | 43 | __set_errno (ENOENT); |
46f894d8 | 44 | return true; |
28f540f4 | 45 | } |
46f894d8 RM |
46 | return false; |
47 | } | |
28f540f4 | 48 | |
46f894d8 RM |
49 | static DIR * |
50 | opendir_tail (int fd) | |
51 | { | |
52 | if (__glibc_unlikely (fd < 0)) | |
4bd8be48 UD |
53 | return NULL; |
54 | ||
46f894d8 RM |
55 | /* Now make sure this really is a directory and nothing changed since the |
56 | `stat' call. The S_ISDIR check is superfluous if O_DIRECTORY works, | |
57 | but it's cheap and we need the stat call for st_blksize anyway. */ | |
58 | struct stat64 statbuf; | |
59 | if (__glibc_unlikely (__fxstat64 (_STAT_VER, fd, &statbuf) < 0)) | |
60 | goto lose; | |
61 | if (__glibc_unlikely (! S_ISDIR (statbuf.st_mode))) | |
4bd8be48 | 62 | { |
46f894d8 RM |
63 | __set_errno (ENOTDIR); |
64 | lose: | |
c181840c | 65 | __close_nocancel_nostatus (fd); |
46f894d8 RM |
66 | return NULL; |
67 | } | |
68 | ||
69 | return __alloc_dir (fd, true, 0, &statbuf); | |
70 | } | |
71 | ||
72 | ||
73 | #if IS_IN (libc) | |
74 | DIR * | |
46f894d8 RM |
75 | __opendirat (int dfd, const char *name) |
76 | { | |
77 | if (__glibc_unlikely (invalid_name (name))) | |
78 | return NULL; | |
79 | ||
0bb2fabc | 80 | return opendir_tail (__openat_nocancel (dfd, name, opendir_oflags)); |
1812d50b | 81 | } |
46f894d8 | 82 | #endif |
c55fbd1e UD |
83 | |
84 | ||
85 | /* Open a directory stream on NAME. */ | |
86 | DIR * | |
87 | __opendir (const char *name) | |
88 | { | |
46f894d8 RM |
89 | if (__glibc_unlikely (invalid_name (name))) |
90 | return NULL; | |
91 | ||
c2284574 | 92 | return opendir_tail (__open_nocancel (name, opendir_oflags)); |
c55fbd1e | 93 | } |
1812d50b UD |
94 | weak_alias (__opendir, opendir) |
95 | ||
1812d50b | 96 | DIR * |
28377d1b | 97 | __alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp) |
1812d50b | 98 | { |
cef9b653 FW |
99 | /* We have to set the close-on-exit flag if the user provided the |
100 | file descriptor. */ | |
101 | if (!close_fd | |
06ab719d | 102 | && __glibc_unlikely (__fcntl64_nocancel (fd, F_SETFD, FD_CLOEXEC) < 0)) |
cbf0489b | 103 | goto lose; |
28f540f4 | 104 | |
62f63c47 UD |
105 | const size_t default_allocation = (4 * BUFSIZ < sizeof (struct dirent64) |
106 | ? sizeof (struct dirent64) : 4 * BUFSIZ); | |
107 | const size_t small_allocation = (BUFSIZ < sizeof (struct dirent64) | |
108 | ? sizeof (struct dirent64) : BUFSIZ); | |
109 | size_t allocation = default_allocation; | |
28f540f4 | 110 | #ifdef _STATBUF_ST_BLKSIZE |
172a631a FW |
111 | /* Increase allocation if requested, but not if the value appears to |
112 | be bogus. */ | |
113 | if (statp != NULL) | |
114 | allocation = MIN (MAX ((size_t) statp->st_blksize, default_allocation), | |
115 | MAX_DIR_BUFFER_SIZE); | |
28f540f4 | 116 | #endif |
9eecb5e8 | 117 | |
fa39685d | 118 | DIR *dirp = (DIR *) malloc (sizeof (DIR) + allocation); |
9eecb5e8 | 119 | if (dirp == NULL) |
28f540f4 | 120 | { |
62f63c47 UD |
121 | allocation = small_allocation; |
122 | dirp = (DIR *) malloc (sizeof (DIR) + allocation); | |
123 | ||
221e5230 | 124 | if (dirp == NULL) |
221e5230 UD |
125 | lose: |
126 | { | |
127 | if (close_fd) | |
128 | { | |
129 | int save_errno = errno; | |
c181840c | 130 | __close_nocancel_nostatus (fd); |
221e5230 UD |
131 | __set_errno (save_errno); |
132 | } | |
133 | return NULL; | |
18b8e054 | 134 | } |
28f540f4 | 135 | } |
c1509239 | 136 | |
c8ccd8e3 | 137 | dirp->fd = fd; |
4f41c682 | 138 | #if IS_IN (libc) |
0d204b0a | 139 | __libc_lock_init (dirp->lock); |
858ee15d | 140 | #endif |
c8ccd8e3 | 141 | dirp->allocation = allocation; |
fa39685d UD |
142 | dirp->size = 0; |
143 | dirp->offset = 0; | |
144 | dirp->filepos = 0; | |
91ce4085 | 145 | dirp->errcode = 0; |
c1509239 | 146 | |
28f540f4 RM |
147 | return dirp; |
148 | } |