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 #if defined(LIBC_SCCS) && !defined(lint) 33 static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 34 #endif /* LIBC_SCCS and not lint */ 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include "namespace.h" 39 #include <sys/param.h> 40 #include <sys/mount.h> 41 #include <sys/stat.h> 42 43 #include <dirent.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include "un-namespace.h" 50 51 #include "gen-private.h" 52 #include "telldir.h" 53 54 static DIR * __opendir_common(int, int, bool); 55 56 /* 57 * Open a directory. 58 */ 59 DIR * 60 opendir(const char *name) 61 { 62 63 return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 64 } 65 66 /* 67 * Open a directory with existing file descriptor. 68 */ 69 DIR * 70 fdopendir(int fd) 71 { 72 73 if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 74 return (NULL); 75 return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP, true)); 76 } 77 78 DIR * 79 __opendir2(const char *name, int flags) 80 { 81 int fd; 82 DIR *dir; 83 int saved_errno; 84 85 if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0) 86 return (NULL); 87 if ((fd = _open(name, 88 O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) 89 return (NULL); 90 91 dir = __opendir_common(fd, flags, false); 92 if (dir == NULL) { 93 saved_errno = errno; 94 _close(fd); 95 errno = saved_errno; 96 } 97 return (dir); 98 } 99 100 static int 101 opendir_compar(const void *p1, const void *p2) 102 { 103 104 return (strcmp((*(const struct dirent **)p1)->d_name, 105 (*(const struct dirent **)p2)->d_name)); 106 } 107 108 /* 109 * For a directory at the top of a unionfs stack, the entire directory's 110 * contents are read and cached locally until the next call to rewinddir(). 111 * For the fdopendir() case, the initial seek position must be preserved. 112 * For rewinddir(), the full directory should always be re-read from the 113 * beginning. 114 * 115 * If an error occurs, the existing buffer and state of 'dirp' is left 116 * unchanged. 117 */ 118 bool 119 _filldir(DIR *dirp, bool use_current_pos) 120 { 121 struct dirent **dpv; 122 char *buf, *ddptr, *ddeptr; 123 off_t pos; 124 int fd2, incr, len, n, saved_errno, space; 125 126 len = 0; 127 space = 0; 128 buf = NULL; 129 ddptr = NULL; 130 131 /* 132 * Use the system page size if that is a multiple of DIRBLKSIZ. 133 * Hopefully this can be a big win someday by allowing page 134 * trades to user space to be done by _getdirentries(). 135 */ 136 incr = getpagesize(); 137 if ((incr % DIRBLKSIZ) != 0) 138 incr = DIRBLKSIZ; 139 140 /* 141 * The strategy here is to read all the directory 142 * entries into a buffer, sort the buffer, and 143 * remove duplicate entries by setting the inode 144 * number to zero. 145 * 146 * We reopen the directory because _getdirentries() 147 * on a MNT_UNION mount modifies the open directory, 148 * making it refer to the lower directory after the 149 * upper directory's entries are exhausted. 150 * This would otherwise break software that uses 151 * the directory descriptor for fchdir or *at 152 * functions, such as fts.c. 153 */ 154 if ((fd2 = _openat(dirp->dd_fd, ".", O_RDONLY | O_CLOEXEC)) == -1) 155 return (false); 156 157 if (use_current_pos) { 158 pos = lseek(dirp->dd_fd, 0, SEEK_CUR); 159 if (pos == -1 || lseek(fd2, pos, SEEK_SET) == -1) { 160 saved_errno = errno; 161 _close(fd2); 162 errno = saved_errno; 163 return (false); 164 } 165 } 166 167 do { 168 /* 169 * Always make at least DIRBLKSIZ bytes 170 * available to _getdirentries 171 */ 172 if (space < DIRBLKSIZ) { 173 space += incr; 174 len += incr; 175 buf = reallocf(buf, len); 176 if (buf == NULL) { 177 saved_errno = errno; 178 _close(fd2); 179 errno = saved_errno; 180 return (false); 181 } 182 ddptr = buf + (len - space); 183 } 184 185 n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek); 186 if (n > 0) { 187 ddptr += n; 188 space -= n; 189 } 190 if (n < 0) { 191 saved_errno = errno; 192 _close(fd2); 193 errno = saved_errno; 194 return (false); 195 } 196 } while (n > 0); 197 _close(fd2); 198 199 ddeptr = ddptr; 200 201 /* 202 * There is now a buffer full of (possibly) duplicate 203 * names. 204 */ 205 dirp->dd_buf = buf; 206 207 /* 208 * Go round this loop twice... 209 * 210 * Scan through the buffer, counting entries. 211 * On the second pass, save pointers to each one. 212 * Then sort the pointers and remove duplicate names. 213 */ 214 for (dpv = NULL;;) { 215 n = 0; 216 ddptr = buf; 217 while (ddptr < ddeptr) { 218 struct dirent *dp; 219 220 dp = (struct dirent *) ddptr; 221 if ((long)dp & 03L) 222 break; 223 if ((dp->d_reclen <= 0) || 224 (dp->d_reclen > (ddeptr + 1 - ddptr))) 225 break; 226 ddptr += dp->d_reclen; 227 if (dp->d_fileno) { 228 if (dpv) 229 dpv[n] = dp; 230 n++; 231 } 232 } 233 234 if (dpv) { 235 struct dirent *xp; 236 237 /* 238 * This sort must be stable. 239 */ 240 mergesort(dpv, n, sizeof(*dpv), opendir_compar); 241 242 dpv[n] = NULL; 243 xp = NULL; 244 245 /* 246 * Scan through the buffer in sort order, 247 * zapping the inode number of any 248 * duplicate names. 249 */ 250 for (n = 0; dpv[n]; n++) { 251 struct dirent *dp = dpv[n]; 252 253 if ((xp == NULL) || 254 strcmp(dp->d_name, xp->d_name)) { 255 xp = dp; 256 } else { 257 dp->d_fileno = 0; 258 } 259 if (dp->d_type == DT_WHT && 260 (dirp->dd_flags & DTF_HIDEW)) 261 dp->d_fileno = 0; 262 } 263 264 free(dpv); 265 break; 266 } else { 267 dpv = malloc((n+1) * sizeof(struct dirent *)); 268 if (dpv == NULL) 269 break; 270 } 271 } 272 273 dirp->dd_len = len; 274 dirp->dd_size = ddptr - dirp->dd_buf; 275 return (true); 276 } 277 278 279 /* 280 * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 281 */ 282 static DIR * 283 __opendir_common(int fd, int flags, bool use_current_pos) 284 { 285 DIR *dirp; 286 int incr; 287 int saved_errno; 288 int unionstack; 289 290 if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 291 return (NULL); 292 293 dirp->dd_buf = NULL; 294 dirp->dd_fd = fd; 295 dirp->dd_flags = flags; 296 dirp->dd_loc = 0; 297 dirp->dd_lock = NULL; 298 dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 299 LIST_INIT(&dirp->dd_td->td_locq); 300 dirp->dd_td->td_loccnt = 0; 301 dirp->dd_compat_de = NULL; 302 303 /* 304 * Use the system page size if that is a multiple of DIRBLKSIZ. 305 * Hopefully this can be a big win someday by allowing page 306 * trades to user space to be done by _getdirentries(). 307 */ 308 incr = getpagesize(); 309 if ((incr % DIRBLKSIZ) != 0) 310 incr = DIRBLKSIZ; 311 312 /* 313 * Determine whether this directory is the top of a union stack. 314 */ 315 if (flags & DTF_NODUP) { 316 struct statfs sfb; 317 318 if (_fstatfs(fd, &sfb) < 0) 319 goto fail; 320 unionstack = !strcmp(sfb.f_fstypename, "unionfs") 321 || (sfb.f_flags & MNT_UNION); 322 } else { 323 unionstack = 0; 324 } 325 326 if (unionstack) { 327 if (!_filldir(dirp, use_current_pos)) 328 goto fail; 329 dirp->dd_flags |= __DTF_READALL; 330 } else { 331 dirp->dd_len = incr; 332 dirp->dd_buf = malloc(dirp->dd_len); 333 if (dirp->dd_buf == NULL) 334 goto fail; 335 if (use_current_pos) { 336 /* 337 * Read the first batch of directory entries 338 * to prime dd_seek. This also checks if the 339 * fd passed to fdopendir() is a directory. 340 */ 341 dirp->dd_size = _getdirentries(dirp->dd_fd, 342 dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); 343 if (dirp->dd_size < 0) { 344 if (errno == EINVAL) 345 errno = ENOTDIR; 346 goto fail; 347 } 348 dirp->dd_flags |= __DTF_SKIPREAD; 349 } else { 350 dirp->dd_size = 0; 351 dirp->dd_seek = 0; 352 } 353 } 354 355 return (dirp); 356 357 fail: 358 saved_errno = errno; 359 free(dirp->dd_buf); 360 free(dirp); 361 errno = saved_errno; 362 return (NULL); 363 } 364