1123059Ssimon/*- 2123016Sphk * Copyright (c) 1983, 1993 3123016Sphk * The Regents of the University of California. All rights reserved. 4123016Sphk * 5123016Sphk * Redistribution and use in source and binary forms, with or without 6123016Sphk * modification, are permitted provided that the following conditions 7123016Sphk * are met: 8123016Sphk * 1. Redistributions of source code must retain the above copyright 9123016Sphk * notice, this list of conditions and the following disclaimer. 10123016Sphk * 2. Redistributions in binary form must reproduce the above copyright 11123016Sphk * notice, this list of conditions and the following disclaimer in the 12123016Sphk * documentation and/or other materials provided with the distribution. 13123016Sphk * 4. Neither the name of the University nor the names of its contributors 14123016Sphk * may be used to endorse or promote products derived from this software 15123016Sphk * without specific prior written permission. 16123016Sphk * 17123016Sphk * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18123016Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19123016Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20123016Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21123016Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22123016Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23123016Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24123016Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25123016Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26123016Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27123016Sphk * SUCH DAMAGE. 28123059Ssimon */ 29123016Sphk 30123016Sphk#if defined(LIBC_SCCS) && !defined(lint) 31123016Sphkstatic char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 32123016Sphk#endif /* LIBC_SCCS and not lint */ 33123016Sphk#include <sys/cdefs.h> 34123059Ssimon__FBSDID("$FreeBSD: releng/11.0/lib/libc/gen/opendir.c 297790 2016-04-10 19:33:58Z pfg $"); 35123059Ssimon 36123059Ssimon#include "namespace.h" 37123059Ssimon#include <sys/param.h> 38123059Ssimon#include <sys/mount.h> 39123059Ssimon#include <sys/stat.h> 40123059Ssimon 41123059Ssimon#include <dirent.h> 42123059Ssimon#include <errno.h> 43123059Ssimon#include <fcntl.h> 44123016Sphk#include <stdlib.h> 45123059Ssimon#include <string.h> 46123059Ssimon#include <unistd.h> 47123059Ssimon#include "un-namespace.h" 48123016Sphk 49123059Ssimon#include "gen-private.h" 50123059Ssimon#include "telldir.h" 51123059Ssimon 52123016Sphkstatic DIR * __opendir_common(int, int, bool); 53123016Sphk 54123016Sphk/* 55123059Ssimon * Open a directory. 56123059Ssimon */ 57123059SsimonDIR * 58123059Ssimonopendir(const char *name) 59123016Sphk{ 60123059Ssimon 61123059Ssimon return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 62123059Ssimon} 63123016Sphk 64123059Ssimon/* 65123059Ssimon * Open a directory with existing file descriptor. 66123059Ssimon */ 67123016SphkDIR * 68123016Sphkfdopendir(int fd) 69123016Sphk{ 70123059Ssimon 71123059Ssimon if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 72123059Ssimon return (NULL); 73123016Sphk return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP, true)); 74123016Sphk} 75123059Ssimon 76123059SsimonDIR * 77123059Ssimon__opendir2(const char *name, int flags) 78123059Ssimon{ 79123059Ssimon int fd; 80123059Ssimon DIR *dir; 81123016Sphk int saved_errno; 82123016Sphk 83123059Ssimon if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0) 84123059Ssimon return (NULL); 85123059Ssimon if ((fd = _open(name, 86123016Sphk O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) 87123059Ssimon return (NULL); 88123059Ssimon 89123059Ssimon dir = __opendir_common(fd, flags, false); 90123059Ssimon if (dir == NULL) { 91123016Sphk saved_errno = errno; 92123016Sphk _close(fd); 93123059Ssimon errno = saved_errno; 94123059Ssimon } 95123059Ssimon return (dir); 96123059Ssimon} 97123059Ssimon 98123059Ssimonstatic int 99123059Ssimonopendir_compar(const void *p1, const void *p2) 100123059Ssimon{ 101123059Ssimon 102123016Sphk return (strcmp((*(const struct dirent **)p1)->d_name, 103123016Sphk (*(const struct dirent **)p2)->d_name)); 104123059Ssimon} 105123059Ssimon 106123059Ssimon/* 107123059Ssimon * For a directory at the top of a unionfs stack, the entire directory's 108123059Ssimon * contents are read and cached locally until the next call to rewinddir(). 109123016Sphk * For the fdopendir() case, the initial seek position must be preserved. 110123016Sphk * For rewinddir(), the full directory should always be re-read from the 111123016Sphk * beginning. 112123016Sphk * 113123059Ssimon * If an error occurs, the existing buffer and state of 'dirp' is left 114123016Sphk * unchanged. 115123059Ssimon */ 116123059Ssimonbool 117123059Ssimon_filldir(DIR *dirp, bool use_current_pos) 118123059Ssimon{ 119123059Ssimon struct dirent **dpv; 120123059Ssimon char *buf, *ddptr, *ddeptr; 121123016Sphk off_t pos; 122123016Sphk int fd2, incr, len, n, saved_errno, space; 123123016Sphk 124123059Ssimon len = 0; 125123059Ssimon space = 0; 126123059Ssimon buf = NULL; 127123059Ssimon ddptr = NULL; 128123016Sphk 129123059Ssimon /* 130123059Ssimon * Use the system page size if that is a multiple of DIRBLKSIZ. 131123059Ssimon * Hopefully this can be a big win someday by allowing page 132123059Ssimon * trades to user space to be done by _getdirentries(). 133123059Ssimon */ 134123059Ssimon incr = getpagesize(); 135123059Ssimon if ((incr % DIRBLKSIZ) != 0) 136123059Ssimon incr = DIRBLKSIZ; 137123016Sphk 138123016Sphk /* 139123016Sphk * The strategy here is to read all the directory 140123059Ssimon * entries into a buffer, sort the buffer, and 141123059Ssimon * remove duplicate entries by setting the inode 142123016Sphk * number to zero. 143123059Ssimon * 144123059Ssimon * We reopen the directory because _getdirentries() 145123059Ssimon * on a MNT_UNION mount modifies the open directory, 146123059Ssimon * making it refer to the lower directory after the 147123059Ssimon * upper directory's entries are exhausted. 148123016Sphk * This would otherwise break software that uses 149123016Sphk * the directory descriptor for fchdir or *at 150123059Ssimon * functions, such as fts.c. 151123059Ssimon */ 152123016Sphk if ((fd2 = _openat(dirp->dd_fd, ".", O_RDONLY | O_CLOEXEC)) == -1) 153123016Sphk return (false); 154123016Sphk 155123016Sphk if (use_current_pos) { 156123059Ssimon pos = lseek(dirp->dd_fd, 0, SEEK_CUR); 157123016Sphk if (pos == -1 || lseek(fd2, pos, SEEK_SET) == -1) { 158123016Sphk 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 */ 280static 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 300 /* 301 * Use the system page size if that is a multiple of DIRBLKSIZ. 302 * Hopefully this can be a big win someday by allowing page 303 * trades to user space to be done by _getdirentries(). 304 */ 305 incr = getpagesize(); 306 if ((incr % DIRBLKSIZ) != 0) 307 incr = DIRBLKSIZ; 308 309 /* 310 * Determine whether this directory is the top of a union stack. 311 */ 312 if (flags & DTF_NODUP) { 313 struct statfs sfb; 314 315 if (_fstatfs(fd, &sfb) < 0) 316 goto fail; 317 unionstack = !strcmp(sfb.f_fstypename, "unionfs") 318 || (sfb.f_flags & MNT_UNION); 319 } else { 320 unionstack = 0; 321 } 322 323 if (unionstack) { 324 if (!_filldir(dirp, use_current_pos)) 325 goto fail; 326 dirp->dd_flags |= __DTF_READALL; 327 } else { 328 dirp->dd_len = incr; 329 dirp->dd_buf = malloc(dirp->dd_len); 330 if (dirp->dd_buf == NULL) 331 goto fail; 332 if (use_current_pos) { 333 /* 334 * Read the first batch of directory entries 335 * to prime dd_seek. This also checks if the 336 * fd passed to fdopendir() is a directory. 337 */ 338 dirp->dd_size = _getdirentries(dirp->dd_fd, 339 dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); 340 if (dirp->dd_size < 0) { 341 if (errno == EINVAL) 342 errno = ENOTDIR; 343 goto fail; 344 } 345 dirp->dd_flags |= __DTF_SKIPREAD; 346 } else { 347 dirp->dd_size = 0; 348 dirp->dd_seek = 0; 349 } 350 } 351 352 return (dirp); 353 354fail: 355 saved_errno = errno; 356 free(dirp->dd_buf); 357 free(dirp); 358 errno = saved_errno; 359 return (NULL); 360} 361