1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __SCCSID("@(#)opendir.c 8.8 (Berkeley) 5/1/95"); 34 __FBSDID("$FreeBSD$"); 35 36 #include "namespace.h" 37 #include <sys/param.h> 38 39 #include <dirent.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include "un-namespace.h" 46 47 #include "gen-private.h" 48 #include "telldir.h" 49 50 static DIR * __opendir_common(int, int, bool); 51 52 /* 53 * Open a directory. 54 */ 55 DIR * 56 opendir(const char *name) 57 { 58 59 return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 60 } 61 62 /* 63 * Open a directory with existing file descriptor. 64 */ 65 DIR * 66 fdopendir(int fd) 67 { 68 69 if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 70 return (NULL); 71 return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP, true)); 72 } 73 74 DIR * 75 __opendir2(const char *name, int flags) 76 { 77 int fd; 78 DIR *dir; 79 int saved_errno; 80 81 if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0) 82 return (NULL); 83 if ((fd = _open(name, 84 O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) 85 return (NULL); 86 87 dir = __opendir_common(fd, flags, false); 88 if (dir == NULL) { 89 saved_errno = errno; 90 _close(fd); 91 errno = saved_errno; 92 } 93 return (dir); 94 } 95 96 static int 97 opendir_compar(const void *p1, const void *p2) 98 { 99 100 return (strcmp((*(const struct dirent * const *)p1)->d_name, 101 (*(const struct dirent * const *)p2)->d_name)); 102 } 103 104 /* 105 * For a directory at the top of a unionfs stack, the entire directory's 106 * contents are read and cached locally until the next call to rewinddir(). 107 * For the fdopendir() case, the initial seek position must be preserved. 108 * For rewinddir(), the full directory should always be re-read from the 109 * beginning. 110 * 111 * If an error occurs, the existing buffer and state of 'dirp' is left 112 * unchanged. 113 */ 114 bool 115 _filldir(DIR *dirp, bool use_current_pos) 116 { 117 struct dirent **dpv; 118 char *buf, *ddptr, *ddeptr; 119 off_t pos; 120 int fd2, incr, len, n, saved_errno, space; 121 122 len = 0; 123 space = 0; 124 buf = NULL; 125 ddptr = NULL; 126 127 /* 128 * Use the system page size if that is a multiple of DIRBLKSIZ. 129 * Hopefully this can be a big win someday by allowing page 130 * trades to user space to be done by _getdirentries(). 131 */ 132 incr = getpagesize(); 133 if ((incr % DIRBLKSIZ) != 0) 134 incr = DIRBLKSIZ; 135 136 /* 137 * The strategy here is to read all the directory 138 * entries into a buffer, sort the buffer, and 139 * remove duplicate entries by setting the inode 140 * number to zero. 141 * 142 * We reopen the directory because _getdirentries() 143 * on a MNT_UNION mount modifies the open directory, 144 * making it refer to the lower directory after the 145 * upper directory's entries are exhausted. 146 * This would otherwise break software that uses 147 * the directory descriptor for fchdir or *at 148 * functions, such as fts.c. 149 */ 150 if ((fd2 = _openat(dirp->dd_fd, ".", O_RDONLY | O_CLOEXEC)) == -1) 151 return (false); 152 153 if (use_current_pos) { 154 pos = lseek(dirp->dd_fd, 0, SEEK_CUR); 155 if (pos == -1 || lseek(fd2, pos, SEEK_SET) == -1) { 156 saved_errno = errno; 157 _close(fd2); 158 errno = saved_errno; 159 return (false); 160 } 161 } 162 163 do { 164 /* 165 * Always make at least DIRBLKSIZ bytes 166 * available to _getdirentries 167 */ 168 if (space < DIRBLKSIZ) { 169 space += incr; 170 len += incr; 171 buf = reallocf(buf, len); 172 if (buf == NULL) { 173 saved_errno = errno; 174 _close(fd2); 175 errno = saved_errno; 176 return (false); 177 } 178 ddptr = buf + (len - space); 179 } 180 181 n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek); 182 if (n > 0) { 183 ddptr += n; 184 space -= n; 185 } 186 if (n < 0) { 187 saved_errno = errno; 188 _close(fd2); 189 errno = saved_errno; 190 return (false); 191 } 192 } while (n > 0); 193 _close(fd2); 194 195 ddeptr = ddptr; 196 197 /* 198 * There is now a buffer full of (possibly) duplicate 199 * names. 200 */ 201 dirp->dd_buf = buf; 202 203 /* 204 * Go round this loop twice... 205 * 206 * Scan through the buffer, counting entries. 207 * On the second pass, save pointers to each one. 208 * Then sort the pointers and remove duplicate names. 209 */ 210 for (dpv = NULL;;) { 211 n = 0; 212 ddptr = buf; 213 while (ddptr < ddeptr) { 214 struct dirent *dp; 215 216 dp = (struct dirent *) ddptr; 217 if ((long)dp & 03L) 218 break; 219 if ((dp->d_reclen <= 0) || 220 (dp->d_reclen > (ddeptr + 1 - ddptr))) 221 break; 222 ddptr += dp->d_reclen; 223 if (dp->d_fileno) { 224 if (dpv) 225 dpv[n] = dp; 226 n++; 227 } 228 } 229 230 if (dpv) { 231 struct dirent *xp; 232 233 /* 234 * This sort must be stable. 235 */ 236 mergesort(dpv, n, sizeof(*dpv), opendir_compar); 237 238 dpv[n] = NULL; 239 xp = NULL; 240 241 /* 242 * Scan through the buffer in sort order, 243 * zapping the inode number of any 244 * duplicate names. 245 */ 246 for (n = 0; dpv[n]; n++) { 247 struct dirent *dp = dpv[n]; 248 249 if ((xp == NULL) || 250 strcmp(dp->d_name, xp->d_name)) { 251 xp = dp; 252 } else { 253 dp->d_fileno = 0; 254 } 255 if (dp->d_type == DT_WHT && 256 (dirp->dd_flags & DTF_HIDEW)) 257 dp->d_fileno = 0; 258 } 259 260 free(dpv); 261 break; 262 } else { 263 dpv = malloc((n+1) * sizeof(struct dirent *)); 264 if (dpv == NULL) 265 break; 266 } 267 } 268 269 dirp->dd_len = len; 270 dirp->dd_size = ddptr - dirp->dd_buf; 271 return (true); 272 } 273 274 static bool 275 is_unionstack(int fd) 276 { 277 int unionstack; 278 279 unionstack = _fcntl(fd, F_ISUNIONSTACK, 0); 280 if (unionstack != -1) 281 return (unionstack); 282 283 /* 284 * Should not happen unless running on a kernel without the op, 285 * but no use rendering the system useless in such a case. 286 */ 287 return (0); 288 } 289 290 /* 291 * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 292 */ 293 static DIR * 294 __opendir_common(int fd, int flags, bool use_current_pos) 295 { 296 DIR *dirp; 297 int incr; 298 int saved_errno; 299 bool unionstack; 300 301 if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 302 return (NULL); 303 304 dirp->dd_buf = NULL; 305 dirp->dd_fd = fd; 306 dirp->dd_flags = flags; 307 dirp->dd_loc = 0; 308 dirp->dd_lock = NULL; 309 dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 310 LIST_INIT(&dirp->dd_td->td_locq); 311 dirp->dd_td->td_loccnt = 0; 312 dirp->dd_compat_de = NULL; 313 314 /* 315 * Use the system page size if that is a multiple of DIRBLKSIZ. 316 * Hopefully this can be a big win someday by allowing page 317 * trades to user space to be done by _getdirentries(). 318 */ 319 incr = getpagesize(); 320 if ((incr % DIRBLKSIZ) != 0) 321 incr = DIRBLKSIZ; 322 323 /* 324 * Determine whether this directory is the top of a union stack. 325 */ 326 unionstack = false; 327 if (flags & DTF_NODUP) { 328 unionstack = is_unionstack(fd); 329 } 330 331 if (unionstack) { 332 if (!_filldir(dirp, use_current_pos)) 333 goto fail; 334 dirp->dd_flags |= __DTF_READALL; 335 } else { 336 dirp->dd_len = incr; 337 dirp->dd_buf = malloc(dirp->dd_len); 338 if (dirp->dd_buf == NULL) 339 goto fail; 340 if (use_current_pos) { 341 /* 342 * Read the first batch of directory entries 343 * to prime dd_seek. This also checks if the 344 * fd passed to fdopendir() is a directory. 345 */ 346 dirp->dd_size = _getdirentries(dirp->dd_fd, 347 dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); 348 if (dirp->dd_size < 0) { 349 if (errno == EINVAL) 350 errno = ENOTDIR; 351 goto fail; 352 } 353 dirp->dd_flags |= __DTF_SKIPREAD; 354 } else { 355 dirp->dd_size = 0; 356 dirp->dd_seek = 0; 357 } 358 } 359 360 return (dirp); 361 362 fail: 363 saved_errno = errno; 364 free(dirp->dd_buf); 365 free(dirp); 366 errno = saved_errno; 367 return (NULL); 368 } 369