]>
Commit | Line | Data |
---|---|---|
6d7e8eda | 1 | /* Copyright (C) 1991-2023 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 | ||
cef9b653 | 26 | enum { |
8e4754ed | 27 | opendir_oflags = O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC |
cef9b653 | 28 | }; |
9ffbb612 | 29 | |
46f894d8 RM |
30 | static bool |
31 | invalid_name (const char *name) | |
28f540f4 | 32 | { |
46f894d8 | 33 | if (__glibc_unlikely (name[0] == '\0')) |
28f540f4 RM |
34 | { |
35 | /* POSIX.1-1990 says an empty name gets ENOENT; | |
36 | but `open' might like it fine. */ | |
c4029823 | 37 | __set_errno (ENOENT); |
46f894d8 | 38 | return true; |
28f540f4 | 39 | } |
46f894d8 RM |
40 | return false; |
41 | } | |
28f540f4 | 42 | |
46f894d8 RM |
43 | static DIR * |
44 | opendir_tail (int fd) | |
45 | { | |
46 | if (__glibc_unlikely (fd < 0)) | |
4bd8be48 UD |
47 | return NULL; |
48 | ||
46f894d8 RM |
49 | /* Now make sure this really is a directory and nothing changed since the |
50 | `stat' call. The S_ISDIR check is superfluous if O_DIRECTORY works, | |
51 | but it's cheap and we need the stat call for st_blksize anyway. */ | |
52a5fe70 AZ |
52 | struct __stat64_t64 statbuf; |
53 | if (__glibc_unlikely (__fstat64_time64 (fd, &statbuf) < 0)) | |
46f894d8 RM |
54 | goto lose; |
55 | if (__glibc_unlikely (! S_ISDIR (statbuf.st_mode))) | |
4bd8be48 | 56 | { |
46f894d8 RM |
57 | __set_errno (ENOTDIR); |
58 | lose: | |
c181840c | 59 | __close_nocancel_nostatus (fd); |
46f894d8 RM |
60 | return NULL; |
61 | } | |
62 | ||
63 | return __alloc_dir (fd, true, 0, &statbuf); | |
64 | } | |
65 | ||
66 | ||
67 | #if IS_IN (libc) | |
68 | DIR * | |
46f894d8 RM |
69 | __opendirat (int dfd, const char *name) |
70 | { | |
71 | if (__glibc_unlikely (invalid_name (name))) | |
72 | return NULL; | |
73 | ||
0bb2fabc | 74 | return opendir_tail (__openat_nocancel (dfd, name, opendir_oflags)); |
1812d50b | 75 | } |
46f894d8 | 76 | #endif |
c55fbd1e UD |
77 | |
78 | ||
79 | /* Open a directory stream on NAME. */ | |
80 | DIR * | |
81 | __opendir (const char *name) | |
82 | { | |
46f894d8 RM |
83 | if (__glibc_unlikely (invalid_name (name))) |
84 | return NULL; | |
85 | ||
c2284574 | 86 | return opendir_tail (__open_nocancel (name, opendir_oflags)); |
c55fbd1e | 87 | } |
1812d50b UD |
88 | weak_alias (__opendir, opendir) |
89 | ||
1812d50b | 90 | DIR * |
52a5fe70 AZ |
91 | __alloc_dir (int fd, bool close_fd, int flags, |
92 | const struct __stat64_t64 *statp) | |
1812d50b | 93 | { |
cef9b653 FW |
94 | /* We have to set the close-on-exit flag if the user provided the |
95 | file descriptor. */ | |
96 | if (!close_fd | |
06ab719d | 97 | && __glibc_unlikely (__fcntl64_nocancel (fd, F_SETFD, FD_CLOEXEC) < 0)) |
4b962c9e AZ |
98 | return NULL; |
99 | ||
100 | /* The st_blksize value of the directory is used as a hint for the | |
101 | size of the buffer which receives struct dirent values from the | |
102 | kernel. st_blksize is limited to max_buffer_size, in case the | |
103 | file system provides a bogus value. */ | |
104 | enum { max_buffer_size = 1048576 }; | |
105 | ||
aa783f9a | 106 | enum { allocation_size = 32768 }; |
4b962c9e AZ |
107 | _Static_assert (allocation_size >= sizeof (struct dirent64), |
108 | "allocation_size < sizeof (struct dirent64)"); | |
109 | ||
172a631a | 110 | /* Increase allocation if requested, but not if the value appears to |
4b962c9e | 111 | be bogus. It will be between 32Kb and 1Mb. */ |
aa783f9a FS |
112 | size_t allocation = MIN (MAX ((size_t) statp->st_blksize, (size_t) |
113 | allocation_size), (size_t) max_buffer_size); | |
9eecb5e8 | 114 | |
fa39685d | 115 | DIR *dirp = (DIR *) malloc (sizeof (DIR) + allocation); |
9eecb5e8 | 116 | if (dirp == NULL) |
28f540f4 | 117 | { |
4b962c9e AZ |
118 | if (close_fd) |
119 | __close_nocancel_nostatus (fd); | |
120 | return NULL; | |
28f540f4 | 121 | } |
c1509239 | 122 | |
c8ccd8e3 | 123 | dirp->fd = fd; |
4f41c682 | 124 | #if IS_IN (libc) |
0d204b0a | 125 | __libc_lock_init (dirp->lock); |
858ee15d | 126 | #endif |
c8ccd8e3 | 127 | dirp->allocation = allocation; |
fa39685d UD |
128 | dirp->size = 0; |
129 | dirp->offset = 0; | |
130 | dirp->filepos = 0; | |
91ce4085 | 131 | dirp->errcode = 0; |
c1509239 | 132 | |
28f540f4 RM |
133 | return dirp; |
134 | } |