1f6386c25SXin LI /*- 2*8a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 3*8a16b7a1SPedro F. Giffuni * 458f0484fSRodney W. Grimes * Copyright (c) 1983, 1993 558f0484fSRodney W. Grimes * The Regents of the University of California. All rights reserved. 658f0484fSRodney W. Grimes * 758f0484fSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 858f0484fSRodney W. Grimes * modification, are permitted provided that the following conditions 958f0484fSRodney W. Grimes * are met: 1058f0484fSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 1158f0484fSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 1258f0484fSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 1358f0484fSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 1458f0484fSRodney W. Grimes * documentation and/or other materials provided with the distribution. 15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 1658f0484fSRodney W. Grimes * may be used to endorse or promote products derived from this software 1758f0484fSRodney W. Grimes * without specific prior written permission. 1858f0484fSRodney W. Grimes * 1958f0484fSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2058f0484fSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2158f0484fSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2258f0484fSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2358f0484fSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2458f0484fSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2558f0484fSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2658f0484fSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2758f0484fSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2858f0484fSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2958f0484fSRodney W. Grimes * SUCH DAMAGE. 3058f0484fSRodney W. Grimes */ 3158f0484fSRodney W. Grimes 3258f0484fSRodney W. Grimes #if defined(LIBC_SCCS) && !defined(lint) 33adf6ad9eSPeter Wemm static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 3458f0484fSRodney W. Grimes #endif /* LIBC_SCCS and not lint */ 35ea8d448aSDavid E. O'Brien #include <sys/cdefs.h> 36ea8d448aSDavid E. O'Brien __FBSDID("$FreeBSD$"); 3758f0484fSRodney W. Grimes 38d201fe46SDaniel Eischen #include "namespace.h" 3958f0484fSRodney W. Grimes #include <sys/param.h> 40adf6ad9eSPeter Wemm #include <sys/mount.h> 4103dcee8dSBruce Evans #include <sys/stat.h> 4258f0484fSRodney W. Grimes 4358f0484fSRodney W. Grimes #include <dirent.h> 4455e2b2c6SBruce Evans #include <errno.h> 4558f0484fSRodney W. Grimes #include <fcntl.h> 4658f0484fSRodney W. Grimes #include <stdlib.h> 4781b3ad59STim J. Robbins #include <string.h> 4858f0484fSRodney W. Grimes #include <unistd.h> 49d201fe46SDaniel Eischen #include "un-namespace.h" 5058f0484fSRodney W. Grimes 510bb2aabfSGleb Kurtsou #include "gen-private.h" 5210d1cba0SDaniel Eischen #include "telldir.h" 536fda52baSXin LI 549f72c032SJohn Baldwin static DIR * __opendir_common(int, int, bool); 556fda52baSXin LI 5658f0484fSRodney W. Grimes /* 57adf6ad9eSPeter Wemm * Open a directory. 5858f0484fSRodney W. Grimes */ 5958f0484fSRodney W. Grimes DIR * 60f6386c25SXin LI opendir(const char *name) 6158f0484fSRodney W. Grimes { 628bb47e40SDavid E. O'Brien 63adf6ad9eSPeter Wemm return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 64adf6ad9eSPeter Wemm } 65adf6ad9eSPeter Wemm 666fda52baSXin LI /* 676fda52baSXin LI * Open a directory with existing file descriptor. 686fda52baSXin LI */ 696fda52baSXin LI DIR * 706fda52baSXin LI fdopendir(int fd) 716fda52baSXin LI { 726fda52baSXin LI 7301e49397SRuslan Ermilov if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 7401e49397SRuslan Ermilov return (NULL); 759f72c032SJohn Baldwin return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP, true)); 766fda52baSXin LI } 776fda52baSXin LI 78adf6ad9eSPeter Wemm DIR * 79f6386c25SXin LI __opendir2(const char *name, int flags) 80adf6ad9eSPeter Wemm { 81adf6ad9eSPeter Wemm int fd; 8238574aa8SJilles Tjoelker DIR *dir; 8338574aa8SJilles Tjoelker int saved_errno; 8458f0484fSRodney W. Grimes 859f72c032SJohn Baldwin if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0) 869f72c032SJohn Baldwin return (NULL); 8701e49397SRuslan Ermilov if ((fd = _open(name, 8801e49397SRuslan Ermilov O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) 89adf6ad9eSPeter Wemm return (NULL); 906fda52baSXin LI 919f72c032SJohn Baldwin dir = __opendir_common(fd, flags, false); 9238574aa8SJilles Tjoelker if (dir == NULL) { 9338574aa8SJilles Tjoelker saved_errno = errno; 9438574aa8SJilles Tjoelker _close(fd); 9538574aa8SJilles Tjoelker errno = saved_errno; 9638574aa8SJilles Tjoelker } 9738574aa8SJilles Tjoelker return (dir); 986fda52baSXin LI } 996fda52baSXin LI 100f5636f88SKonstantin Belousov static int 101e5c9c853SAndrey A. Chernov opendir_compar(const void *p1, const void *p2) 102f5636f88SKonstantin Belousov { 103f5636f88SKonstantin Belousov 104b9a74f2aSAndrey A. Chernov return (strcmp((*(const struct dirent **)p1)->d_name, 105f5636f88SKonstantin Belousov (*(const struct dirent **)p2)->d_name)); 106f5636f88SKonstantin Belousov } 107f5636f88SKonstantin Belousov 108f5636f88SKonstantin Belousov /* 1099f72c032SJohn Baldwin * For a directory at the top of a unionfs stack, the entire directory's 1109f72c032SJohn Baldwin * contents are read and cached locally until the next call to rewinddir(). 1119f72c032SJohn Baldwin * For the fdopendir() case, the initial seek position must be preserved. 1129f72c032SJohn Baldwin * For rewinddir(), the full directory should always be re-read from the 1139f72c032SJohn Baldwin * beginning. 1149f72c032SJohn Baldwin * 1159f72c032SJohn Baldwin * If an error occurs, the existing buffer and state of 'dirp' is left 1169f72c032SJohn Baldwin * unchanged. 1176fda52baSXin LI */ 1189f72c032SJohn Baldwin bool 1199f72c032SJohn Baldwin _filldir(DIR *dirp, bool use_current_pos) 1206fda52baSXin LI { 1219f72c032SJohn Baldwin struct dirent **dpv; 1229f72c032SJohn Baldwin char *buf, *ddptr, *ddeptr; 1239f72c032SJohn Baldwin off_t pos; 1249f72c032SJohn Baldwin int fd2, incr, len, n, saved_errno, space; 1256fda52baSXin LI 1269f72c032SJohn Baldwin len = 0; 1279f72c032SJohn Baldwin space = 0; 1289f72c032SJohn Baldwin buf = NULL; 1299f72c032SJohn Baldwin ddptr = NULL; 13010d1cba0SDaniel Eischen 13158f0484fSRodney W. Grimes /* 13203dcee8dSBruce Evans * Use the system page size if that is a multiple of DIRBLKSIZ. 133adf6ad9eSPeter Wemm * Hopefully this can be a big win someday by allowing page 134d201fe46SDaniel Eischen * trades to user space to be done by _getdirentries(). 13558f0484fSRodney W. Grimes */ 136adf6ad9eSPeter Wemm incr = getpagesize(); 137adf6ad9eSPeter Wemm if ((incr % DIRBLKSIZ) != 0) 138adf6ad9eSPeter Wemm incr = DIRBLKSIZ; 139adf6ad9eSPeter Wemm 140adf6ad9eSPeter Wemm /* 141adf6ad9eSPeter Wemm * The strategy here is to read all the directory 142adf6ad9eSPeter Wemm * entries into a buffer, sort the buffer, and 143adf6ad9eSPeter Wemm * remove duplicate entries by setting the inode 144adf6ad9eSPeter Wemm * number to zero. 145c6344d08SJilles Tjoelker * 146c6344d08SJilles Tjoelker * We reopen the directory because _getdirentries() 147c6344d08SJilles Tjoelker * on a MNT_UNION mount modifies the open directory, 148c6344d08SJilles Tjoelker * making it refer to the lower directory after the 149c6344d08SJilles Tjoelker * upper directory's entries are exhausted. 150c6344d08SJilles Tjoelker * This would otherwise break software that uses 151c6344d08SJilles Tjoelker * the directory descriptor for fchdir or *at 152c6344d08SJilles Tjoelker * functions, such as fts.c. 153adf6ad9eSPeter Wemm */ 1549f72c032SJohn Baldwin if ((fd2 = _openat(dirp->dd_fd, ".", O_RDONLY | O_CLOEXEC)) == -1) 1559f72c032SJohn Baldwin return (false); 1569f72c032SJohn Baldwin 1579f72c032SJohn Baldwin if (use_current_pos) { 1589f72c032SJohn Baldwin pos = lseek(dirp->dd_fd, 0, SEEK_CUR); 1599f72c032SJohn Baldwin if (pos == -1 || lseek(fd2, pos, SEEK_SET) == -1) { 160c6344d08SJilles Tjoelker saved_errno = errno; 1619f72c032SJohn Baldwin _close(fd2); 162c6344d08SJilles Tjoelker errno = saved_errno; 1639f72c032SJohn Baldwin return (false); 1649f72c032SJohn Baldwin } 165c6344d08SJilles Tjoelker } 166adf6ad9eSPeter Wemm 167adf6ad9eSPeter Wemm do { 168adf6ad9eSPeter Wemm /* 169adf6ad9eSPeter Wemm * Always make at least DIRBLKSIZ bytes 170d201fe46SDaniel Eischen * available to _getdirentries 171adf6ad9eSPeter Wemm */ 172adf6ad9eSPeter Wemm if (space < DIRBLKSIZ) { 173adf6ad9eSPeter Wemm space += incr; 174adf6ad9eSPeter Wemm len += incr; 175e8420087SWarner Losh buf = reallocf(buf, len); 1769f72c032SJohn Baldwin if (buf == NULL) { 1779f72c032SJohn Baldwin saved_errno = errno; 1789f72c032SJohn Baldwin _close(fd2); 1799f72c032SJohn Baldwin errno = saved_errno; 1809f72c032SJohn Baldwin return (false); 1819f72c032SJohn Baldwin } 182adf6ad9eSPeter Wemm ddptr = buf + (len - space); 183adf6ad9eSPeter Wemm } 184adf6ad9eSPeter Wemm 185c6344d08SJilles Tjoelker n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek); 186adf6ad9eSPeter Wemm if (n > 0) { 187adf6ad9eSPeter Wemm ddptr += n; 188adf6ad9eSPeter Wemm space -= n; 189adf6ad9eSPeter Wemm } 1909f72c032SJohn Baldwin if (n < 0) { 1919f72c032SJohn Baldwin saved_errno = errno; 1929f72c032SJohn Baldwin _close(fd2); 1939f72c032SJohn Baldwin errno = saved_errno; 1949f72c032SJohn Baldwin return (false); 1959f72c032SJohn Baldwin } 196adf6ad9eSPeter Wemm } while (n > 0); 1979f72c032SJohn Baldwin _close(fd2); 198adf6ad9eSPeter Wemm 199adf6ad9eSPeter Wemm ddeptr = ddptr; 200adf6ad9eSPeter Wemm 201adf6ad9eSPeter Wemm /* 202adf6ad9eSPeter Wemm * There is now a buffer full of (possibly) duplicate 203adf6ad9eSPeter Wemm * names. 204adf6ad9eSPeter Wemm */ 205adf6ad9eSPeter Wemm dirp->dd_buf = buf; 206adf6ad9eSPeter Wemm 207adf6ad9eSPeter Wemm /* 208adf6ad9eSPeter Wemm * Go round this loop twice... 209adf6ad9eSPeter Wemm * 210adf6ad9eSPeter Wemm * Scan through the buffer, counting entries. 211adf6ad9eSPeter Wemm * On the second pass, save pointers to each one. 212adf6ad9eSPeter Wemm * Then sort the pointers and remove duplicate names. 213adf6ad9eSPeter Wemm */ 214513004a2SPedro F. Giffuni for (dpv = NULL;;) { 215adf6ad9eSPeter Wemm n = 0; 216adf6ad9eSPeter Wemm ddptr = buf; 217adf6ad9eSPeter Wemm while (ddptr < ddeptr) { 218adf6ad9eSPeter Wemm struct dirent *dp; 219adf6ad9eSPeter Wemm 220adf6ad9eSPeter Wemm dp = (struct dirent *) ddptr; 22184d65005SJohn Birrell if ((long)dp & 03L) 222adf6ad9eSPeter Wemm break; 223adf6ad9eSPeter Wemm if ((dp->d_reclen <= 0) || 224adf6ad9eSPeter Wemm (dp->d_reclen > (ddeptr + 1 - ddptr))) 225adf6ad9eSPeter Wemm break; 226adf6ad9eSPeter Wemm ddptr += dp->d_reclen; 227adf6ad9eSPeter Wemm if (dp->d_fileno) { 228adf6ad9eSPeter Wemm if (dpv) 229adf6ad9eSPeter Wemm dpv[n] = dp; 230adf6ad9eSPeter Wemm n++; 231adf6ad9eSPeter Wemm } 232adf6ad9eSPeter Wemm } 233adf6ad9eSPeter Wemm 234adf6ad9eSPeter Wemm if (dpv) { 235adf6ad9eSPeter Wemm struct dirent *xp; 236adf6ad9eSPeter Wemm 237adf6ad9eSPeter Wemm /* 238adf6ad9eSPeter Wemm * This sort must be stable. 239adf6ad9eSPeter Wemm */ 2409f72c032SJohn Baldwin mergesort(dpv, n, sizeof(*dpv), opendir_compar); 241adf6ad9eSPeter Wemm 242adf6ad9eSPeter Wemm dpv[n] = NULL; 243adf6ad9eSPeter Wemm xp = NULL; 244adf6ad9eSPeter Wemm 245adf6ad9eSPeter Wemm /* 246adf6ad9eSPeter Wemm * Scan through the buffer in sort order, 247adf6ad9eSPeter Wemm * zapping the inode number of any 248adf6ad9eSPeter Wemm * duplicate names. 249adf6ad9eSPeter Wemm */ 250adf6ad9eSPeter Wemm for (n = 0; dpv[n]; n++) { 251adf6ad9eSPeter Wemm struct dirent *dp = dpv[n]; 252adf6ad9eSPeter Wemm 253adf6ad9eSPeter Wemm if ((xp == NULL) || 254adf6ad9eSPeter Wemm strcmp(dp->d_name, xp->d_name)) { 255adf6ad9eSPeter Wemm xp = dp; 256adf6ad9eSPeter Wemm } else { 257adf6ad9eSPeter Wemm dp->d_fileno = 0; 258adf6ad9eSPeter Wemm } 259adf6ad9eSPeter Wemm if (dp->d_type == DT_WHT && 2609f72c032SJohn Baldwin (dirp->dd_flags & DTF_HIDEW)) 261adf6ad9eSPeter Wemm dp->d_fileno = 0; 262adf6ad9eSPeter Wemm } 263adf6ad9eSPeter Wemm 264adf6ad9eSPeter Wemm free(dpv); 265adf6ad9eSPeter Wemm break; 266adf6ad9eSPeter Wemm } else { 267adf6ad9eSPeter Wemm dpv = malloc((n+1) * sizeof(struct dirent *)); 268adf6ad9eSPeter Wemm if (dpv == NULL) 269adf6ad9eSPeter Wemm break; 270adf6ad9eSPeter Wemm } 271adf6ad9eSPeter Wemm } 272adf6ad9eSPeter Wemm 273adf6ad9eSPeter Wemm dirp->dd_len = len; 274adf6ad9eSPeter Wemm dirp->dd_size = ddptr - dirp->dd_buf; 2759f72c032SJohn Baldwin return (true); 2769f72c032SJohn Baldwin } 2779f72c032SJohn Baldwin 2789f72c032SJohn Baldwin 2799f72c032SJohn Baldwin /* 2809f72c032SJohn Baldwin * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 2819f72c032SJohn Baldwin */ 2829f72c032SJohn Baldwin static DIR * 2839f72c032SJohn Baldwin __opendir_common(int fd, int flags, bool use_current_pos) 2849f72c032SJohn Baldwin { 2859f72c032SJohn Baldwin DIR *dirp; 2869f72c032SJohn Baldwin int incr; 2879f72c032SJohn Baldwin int saved_errno; 2889f72c032SJohn Baldwin int unionstack; 2899f72c032SJohn Baldwin 2909f72c032SJohn Baldwin if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 2919f72c032SJohn Baldwin return (NULL); 2929f72c032SJohn Baldwin 2939f72c032SJohn Baldwin dirp->dd_buf = NULL; 2949f72c032SJohn Baldwin dirp->dd_fd = fd; 2959f72c032SJohn Baldwin dirp->dd_flags = flags; 2969f72c032SJohn Baldwin dirp->dd_loc = 0; 2979f72c032SJohn Baldwin dirp->dd_lock = NULL; 2989f72c032SJohn Baldwin dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 2999f72c032SJohn Baldwin LIST_INIT(&dirp->dd_td->td_locq); 3009f72c032SJohn Baldwin dirp->dd_td->td_loccnt = 0; 30169921123SKonstantin Belousov dirp->dd_compat_de = NULL; 3029f72c032SJohn Baldwin 3039f72c032SJohn Baldwin /* 3049f72c032SJohn Baldwin * Use the system page size if that is a multiple of DIRBLKSIZ. 3059f72c032SJohn Baldwin * Hopefully this can be a big win someday by allowing page 3069f72c032SJohn Baldwin * trades to user space to be done by _getdirentries(). 3079f72c032SJohn Baldwin */ 3089f72c032SJohn Baldwin incr = getpagesize(); 3099f72c032SJohn Baldwin if ((incr % DIRBLKSIZ) != 0) 3109f72c032SJohn Baldwin incr = DIRBLKSIZ; 3119f72c032SJohn Baldwin 3129f72c032SJohn Baldwin /* 3139f72c032SJohn Baldwin * Determine whether this directory is the top of a union stack. 3149f72c032SJohn Baldwin */ 3159f72c032SJohn Baldwin if (flags & DTF_NODUP) { 3169f72c032SJohn Baldwin struct statfs sfb; 3179f72c032SJohn Baldwin 3189f72c032SJohn Baldwin if (_fstatfs(fd, &sfb) < 0) 3199f72c032SJohn Baldwin goto fail; 3209f72c032SJohn Baldwin unionstack = !strcmp(sfb.f_fstypename, "unionfs") 3219f72c032SJohn Baldwin || (sfb.f_flags & MNT_UNION); 3229f72c032SJohn Baldwin } else { 3239f72c032SJohn Baldwin unionstack = 0; 3249f72c032SJohn Baldwin } 3259f72c032SJohn Baldwin 3269f72c032SJohn Baldwin if (unionstack) { 3279f72c032SJohn Baldwin if (!_filldir(dirp, use_current_pos)) 3289f72c032SJohn Baldwin goto fail; 3299f72c032SJohn Baldwin dirp->dd_flags |= __DTF_READALL; 330adf6ad9eSPeter Wemm } else { 331adf6ad9eSPeter Wemm dirp->dd_len = incr; 332d71458eeSPoul-Henning Kamp dirp->dd_buf = malloc(dirp->dd_len); 33303dcee8dSBruce Evans if (dirp->dd_buf == NULL) 33403dcee8dSBruce Evans goto fail; 3359f72c032SJohn Baldwin if (use_current_pos) { 3369f72c032SJohn Baldwin /* 3379f72c032SJohn Baldwin * Read the first batch of directory entries 3389f72c032SJohn Baldwin * to prime dd_seek. This also checks if the 3399f72c032SJohn Baldwin * fd passed to fdopendir() is a directory. 3409f72c032SJohn Baldwin */ 3419f72c032SJohn Baldwin dirp->dd_size = _getdirentries(dirp->dd_fd, 3429f72c032SJohn Baldwin dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); 3439f72c032SJohn Baldwin if (dirp->dd_size < 0) { 3449f72c032SJohn Baldwin if (errno == EINVAL) 3459f72c032SJohn Baldwin errno = ENOTDIR; 3469f72c032SJohn Baldwin goto fail; 3479f72c032SJohn Baldwin } 3489f72c032SJohn Baldwin dirp->dd_flags |= __DTF_SKIPREAD; 3499f72c032SJohn Baldwin } else { 3509f72c032SJohn Baldwin dirp->dd_size = 0; 35158f0484fSRodney W. Grimes dirp->dd_seek = 0; 352adf6ad9eSPeter Wemm } 3539f72c032SJohn Baldwin } 35455e2b2c6SBruce Evans 355adf6ad9eSPeter Wemm return (dirp); 35603dcee8dSBruce Evans 35703dcee8dSBruce Evans fail: 35803dcee8dSBruce Evans saved_errno = errno; 3599f72c032SJohn Baldwin free(dirp->dd_buf); 36003dcee8dSBruce Evans free(dirp); 36103dcee8dSBruce Evans errno = saved_errno; 36203dcee8dSBruce Evans return (NULL); 36358f0484fSRodney W. Grimes } 364