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