]>
| Commit | Line | Data |
|---|---|---|
| 26420023 | 1 | /* Copyright (C) 1991-2025 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 | } |