1f6386c25SXin LI /*- 258f0484fSRodney W. Grimes * Copyright (c) 1983, 1993 358f0484fSRodney W. Grimes * The Regents of the University of California. All rights reserved. 458f0484fSRodney W. Grimes * 558f0484fSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 658f0484fSRodney W. Grimes * modification, are permitted provided that the following conditions 758f0484fSRodney W. Grimes * are met: 858f0484fSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 958f0484fSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 1058f0484fSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 1158f0484fSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 1258f0484fSRodney W. Grimes * documentation and/or other materials provided with the distribution. 1358f0484fSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 1458f0484fSRodney W. Grimes * may be used to endorse or promote products derived from this software 1558f0484fSRodney W. Grimes * without specific prior written permission. 1658f0484fSRodney W. Grimes * 1758f0484fSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1858f0484fSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1958f0484fSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2058f0484fSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2158f0484fSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2258f0484fSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2358f0484fSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2458f0484fSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2558f0484fSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2658f0484fSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2758f0484fSRodney W. Grimes * SUCH DAMAGE. 2858f0484fSRodney W. Grimes */ 2958f0484fSRodney W. Grimes 3058f0484fSRodney W. Grimes #if defined(LIBC_SCCS) && !defined(lint) 31adf6ad9eSPeter Wemm static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; 3258f0484fSRodney W. Grimes #endif /* LIBC_SCCS and not lint */ 33ea8d448aSDavid E. O'Brien #include <sys/cdefs.h> 34ea8d448aSDavid E. O'Brien __FBSDID("$FreeBSD$"); 3558f0484fSRodney W. Grimes 36d201fe46SDaniel Eischen #include "namespace.h" 3758f0484fSRodney W. Grimes #include <sys/param.h> 38adf6ad9eSPeter Wemm #include <sys/mount.h> 3903dcee8dSBruce Evans #include <sys/stat.h> 4058f0484fSRodney W. Grimes 4158f0484fSRodney W. Grimes #include <dirent.h> 4255e2b2c6SBruce Evans #include <errno.h> 4358f0484fSRodney W. Grimes #include <fcntl.h> 4458f0484fSRodney W. Grimes #include <stdlib.h> 4581b3ad59STim J. Robbins #include <string.h> 4658f0484fSRodney W. Grimes #include <unistd.h> 47d201fe46SDaniel Eischen #include "un-namespace.h" 4858f0484fSRodney W. Grimes 49*0bb2aabfSGleb Kurtsou #include "gen-private.h" 5010d1cba0SDaniel Eischen #include "telldir.h" 516fda52baSXin LI 526fda52baSXin LI static DIR * __opendir_common(int, const char *, int); 536fda52baSXin LI 5458f0484fSRodney W. Grimes /* 55adf6ad9eSPeter Wemm * Open a directory. 5658f0484fSRodney W. Grimes */ 5758f0484fSRodney W. Grimes DIR * 58f6386c25SXin LI opendir(const char *name) 5958f0484fSRodney W. Grimes { 608bb47e40SDavid E. O'Brien 61adf6ad9eSPeter Wemm return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 62adf6ad9eSPeter Wemm } 63adf6ad9eSPeter Wemm 646fda52baSXin LI /* 656fda52baSXin LI * Open a directory with existing file descriptor. 666fda52baSXin LI */ 676fda52baSXin LI DIR * 686fda52baSXin LI fdopendir(int fd) 696fda52baSXin LI { 7001e49397SRuslan Ermilov struct stat statb; 716fda52baSXin LI 7201e49397SRuslan Ermilov /* Check that fd is associated with a directory. */ 7301e49397SRuslan Ermilov if (_fstat(fd, &statb) != 0) 7401e49397SRuslan Ermilov return (NULL); 7501e49397SRuslan Ermilov if (!S_ISDIR(statb.st_mode)) { 7601e49397SRuslan Ermilov errno = ENOTDIR; 7701e49397SRuslan Ermilov return (NULL); 7801e49397SRuslan Ermilov } 7901e49397SRuslan Ermilov if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 8001e49397SRuslan Ermilov return (NULL); 816fda52baSXin LI return (__opendir_common(fd, NULL, DTF_HIDEW|DTF_NODUP)); 826fda52baSXin LI } 836fda52baSXin LI 84adf6ad9eSPeter Wemm DIR * 85f6386c25SXin LI __opendir2(const char *name, int flags) 86adf6ad9eSPeter Wemm { 87adf6ad9eSPeter Wemm int fd; 8838574aa8SJilles Tjoelker DIR *dir; 8938574aa8SJilles Tjoelker int saved_errno; 9058f0484fSRodney W. Grimes 9101e49397SRuslan Ermilov if ((fd = _open(name, 9201e49397SRuslan Ermilov O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) 93adf6ad9eSPeter Wemm return (NULL); 946fda52baSXin LI 9538574aa8SJilles Tjoelker dir = __opendir_common(fd, name, flags); 9638574aa8SJilles Tjoelker if (dir == NULL) { 9738574aa8SJilles Tjoelker saved_errno = errno; 9838574aa8SJilles Tjoelker _close(fd); 9938574aa8SJilles Tjoelker errno = saved_errno; 10038574aa8SJilles Tjoelker } 10138574aa8SJilles Tjoelker return (dir); 1026fda52baSXin LI } 1036fda52baSXin LI 104f5636f88SKonstantin Belousov static int 105e5c9c853SAndrey A. Chernov opendir_compar(const void *p1, const void *p2) 106f5636f88SKonstantin Belousov { 107f5636f88SKonstantin Belousov 108b9a74f2aSAndrey A. Chernov return (strcmp((*(const struct dirent **)p1)->d_name, 109f5636f88SKonstantin Belousov (*(const struct dirent **)p2)->d_name)); 110f5636f88SKonstantin Belousov } 111f5636f88SKonstantin Belousov 112f5636f88SKonstantin Belousov /* 1136fda52baSXin LI * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 1146fda52baSXin LI */ 1156fda52baSXin LI static DIR * 1166fda52baSXin LI __opendir_common(int fd, const char *name, int flags) 1176fda52baSXin LI { 1186fda52baSXin LI DIR *dirp; 1196fda52baSXin LI int incr; 1206fda52baSXin LI int saved_errno; 1216fda52baSXin LI int unionstack; 12238574aa8SJilles Tjoelker int fd2; 1236fda52baSXin LI 12401e49397SRuslan Ermilov if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 12501e49397SRuslan Ermilov return (NULL); 126adf6ad9eSPeter Wemm 1274eae39bfSStefan Farfeleder dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 12810d1cba0SDaniel Eischen LIST_INIT(&dirp->dd_td->td_locq); 12910d1cba0SDaniel Eischen dirp->dd_td->td_loccnt = 0; 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 * Determine whether this directory is the top of a union stack. 142adf6ad9eSPeter Wemm */ 143adf6ad9eSPeter Wemm if (flags & DTF_NODUP) { 144adf6ad9eSPeter Wemm struct statfs sfb; 145adf6ad9eSPeter Wemm 146d201fe46SDaniel Eischen if (_fstatfs(fd, &sfb) < 0) 14703dcee8dSBruce Evans goto fail; 1486e7988b9STim J. Robbins unionstack = !strcmp(sfb.f_fstypename, "unionfs") 149b2bcd87eSJoerg Wunsch || (sfb.f_flags & MNT_UNION); 150adf6ad9eSPeter Wemm } else { 151adf6ad9eSPeter Wemm unionstack = 0; 152adf6ad9eSPeter Wemm } 153adf6ad9eSPeter Wemm 154adf6ad9eSPeter Wemm if (unionstack) { 155adf6ad9eSPeter Wemm int len = 0; 156adf6ad9eSPeter Wemm int space = 0; 157adf6ad9eSPeter Wemm char *buf = 0; 158adf6ad9eSPeter Wemm char *ddptr = 0; 159adf6ad9eSPeter Wemm char *ddeptr; 160adf6ad9eSPeter Wemm int n; 161adf6ad9eSPeter Wemm struct dirent **dpv; 162adf6ad9eSPeter Wemm 163adf6ad9eSPeter Wemm /* 164adf6ad9eSPeter Wemm * The strategy here is to read all the directory 165adf6ad9eSPeter Wemm * entries into a buffer, sort the buffer, and 166adf6ad9eSPeter Wemm * remove duplicate entries by setting the inode 167adf6ad9eSPeter Wemm * number to zero. 168adf6ad9eSPeter Wemm */ 169adf6ad9eSPeter Wemm 170adf6ad9eSPeter Wemm do { 171adf6ad9eSPeter Wemm /* 172adf6ad9eSPeter Wemm * Always make at least DIRBLKSIZ bytes 173d201fe46SDaniel Eischen * available to _getdirentries 174adf6ad9eSPeter Wemm */ 175adf6ad9eSPeter Wemm if (space < DIRBLKSIZ) { 176adf6ad9eSPeter Wemm space += incr; 177adf6ad9eSPeter Wemm len += incr; 178e8420087SWarner Losh buf = reallocf(buf, len); 17903dcee8dSBruce Evans if (buf == NULL) 18003dcee8dSBruce Evans goto fail; 181adf6ad9eSPeter Wemm ddptr = buf + (len - space); 182adf6ad9eSPeter Wemm } 183adf6ad9eSPeter Wemm 184d201fe46SDaniel Eischen n = _getdirentries(fd, ddptr, space, &dirp->dd_seek); 185adf6ad9eSPeter Wemm if (n > 0) { 186adf6ad9eSPeter Wemm ddptr += n; 187adf6ad9eSPeter Wemm space -= n; 188adf6ad9eSPeter Wemm } 189adf6ad9eSPeter Wemm } while (n > 0); 190adf6ad9eSPeter Wemm 191adf6ad9eSPeter Wemm ddeptr = ddptr; 192adf6ad9eSPeter Wemm flags |= __DTF_READALL; 193adf6ad9eSPeter Wemm 194adf6ad9eSPeter Wemm /* 195adf6ad9eSPeter Wemm * Re-open the directory. 196adf6ad9eSPeter Wemm * This has the effect of rewinding back to the 197adf6ad9eSPeter Wemm * top of the union stack and is needed by 198adf6ad9eSPeter Wemm * programs which plan to fchdir to a descriptor 199adf6ad9eSPeter Wemm * which has also been read -- see fts.c. 200adf6ad9eSPeter Wemm */ 201adf6ad9eSPeter Wemm if (flags & DTF_REWIND) { 20238574aa8SJilles Tjoelker if ((fd2 = _open(name, O_RDONLY | O_DIRECTORY)) == -1) { 20303dcee8dSBruce Evans saved_errno = errno; 204adf6ad9eSPeter Wemm free(buf); 205adf6ad9eSPeter Wemm free(dirp); 20603dcee8dSBruce Evans errno = saved_errno; 207adf6ad9eSPeter Wemm return (NULL); 208adf6ad9eSPeter Wemm } 20938574aa8SJilles Tjoelker (void)_dup2(fd2, fd); 21038574aa8SJilles Tjoelker _close(fd2); 211adf6ad9eSPeter Wemm } 212adf6ad9eSPeter Wemm 213adf6ad9eSPeter Wemm /* 214adf6ad9eSPeter Wemm * There is now a buffer full of (possibly) duplicate 215adf6ad9eSPeter Wemm * names. 216adf6ad9eSPeter Wemm */ 217adf6ad9eSPeter Wemm dirp->dd_buf = buf; 218adf6ad9eSPeter Wemm 219adf6ad9eSPeter Wemm /* 220adf6ad9eSPeter Wemm * Go round this loop twice... 221adf6ad9eSPeter Wemm * 222adf6ad9eSPeter Wemm * Scan through the buffer, counting entries. 223adf6ad9eSPeter Wemm * On the second pass, save pointers to each one. 224adf6ad9eSPeter Wemm * Then sort the pointers and remove duplicate names. 225adf6ad9eSPeter Wemm */ 226adf6ad9eSPeter Wemm for (dpv = 0;;) { 227adf6ad9eSPeter Wemm n = 0; 228adf6ad9eSPeter Wemm ddptr = buf; 229adf6ad9eSPeter Wemm while (ddptr < ddeptr) { 230adf6ad9eSPeter Wemm struct dirent *dp; 231adf6ad9eSPeter Wemm 232adf6ad9eSPeter Wemm dp = (struct dirent *) ddptr; 23384d65005SJohn Birrell if ((long)dp & 03L) 234adf6ad9eSPeter Wemm break; 235adf6ad9eSPeter Wemm if ((dp->d_reclen <= 0) || 236adf6ad9eSPeter Wemm (dp->d_reclen > (ddeptr + 1 - ddptr))) 237adf6ad9eSPeter Wemm break; 238adf6ad9eSPeter Wemm ddptr += dp->d_reclen; 239adf6ad9eSPeter Wemm if (dp->d_fileno) { 240adf6ad9eSPeter Wemm if (dpv) 241adf6ad9eSPeter Wemm dpv[n] = dp; 242adf6ad9eSPeter Wemm n++; 243adf6ad9eSPeter Wemm } 244adf6ad9eSPeter Wemm } 245adf6ad9eSPeter Wemm 246adf6ad9eSPeter Wemm if (dpv) { 247adf6ad9eSPeter Wemm struct dirent *xp; 248adf6ad9eSPeter Wemm 249adf6ad9eSPeter Wemm /* 250adf6ad9eSPeter Wemm * This sort must be stable. 251adf6ad9eSPeter Wemm */ 252f5636f88SKonstantin Belousov mergesort(dpv, n, sizeof(*dpv), 253e5c9c853SAndrey A. Chernov opendir_compar); 254adf6ad9eSPeter Wemm 255adf6ad9eSPeter Wemm dpv[n] = NULL; 256adf6ad9eSPeter Wemm xp = NULL; 257adf6ad9eSPeter Wemm 258adf6ad9eSPeter Wemm /* 259adf6ad9eSPeter Wemm * Scan through the buffer in sort order, 260adf6ad9eSPeter Wemm * zapping the inode number of any 261adf6ad9eSPeter Wemm * duplicate names. 262adf6ad9eSPeter Wemm */ 263adf6ad9eSPeter Wemm for (n = 0; dpv[n]; n++) { 264adf6ad9eSPeter Wemm struct dirent *dp = dpv[n]; 265adf6ad9eSPeter Wemm 266adf6ad9eSPeter Wemm if ((xp == NULL) || 267adf6ad9eSPeter Wemm strcmp(dp->d_name, xp->d_name)) { 268adf6ad9eSPeter Wemm xp = dp; 269adf6ad9eSPeter Wemm } else { 270adf6ad9eSPeter Wemm dp->d_fileno = 0; 271adf6ad9eSPeter Wemm } 272adf6ad9eSPeter Wemm if (dp->d_type == DT_WHT && 273adf6ad9eSPeter Wemm (flags & DTF_HIDEW)) 274adf6ad9eSPeter Wemm dp->d_fileno = 0; 275adf6ad9eSPeter Wemm } 276adf6ad9eSPeter Wemm 277adf6ad9eSPeter Wemm free(dpv); 278adf6ad9eSPeter Wemm break; 279adf6ad9eSPeter Wemm } else { 280adf6ad9eSPeter Wemm dpv = malloc((n+1) * sizeof(struct dirent *)); 281adf6ad9eSPeter Wemm if (dpv == NULL) 282adf6ad9eSPeter Wemm break; 283adf6ad9eSPeter Wemm } 284adf6ad9eSPeter Wemm } 285adf6ad9eSPeter Wemm 286adf6ad9eSPeter Wemm dirp->dd_len = len; 287adf6ad9eSPeter Wemm dirp->dd_size = ddptr - dirp->dd_buf; 288adf6ad9eSPeter Wemm } else { 289adf6ad9eSPeter Wemm dirp->dd_len = incr; 2904da7d0f5SDoug Rabson dirp->dd_size = 0; 291d71458eeSPoul-Henning Kamp dirp->dd_buf = malloc(dirp->dd_len); 29203dcee8dSBruce Evans if (dirp->dd_buf == NULL) 29303dcee8dSBruce Evans goto fail; 29458f0484fSRodney W. Grimes dirp->dd_seek = 0; 295adf6ad9eSPeter Wemm flags &= ~DTF_REWIND; 296adf6ad9eSPeter Wemm } 297adf6ad9eSPeter Wemm 298adf6ad9eSPeter Wemm dirp->dd_loc = 0; 299adf6ad9eSPeter Wemm dirp->dd_fd = fd; 300adf6ad9eSPeter Wemm dirp->dd_flags = flags; 301d201fe46SDaniel Eischen dirp->dd_lock = NULL; 302adf6ad9eSPeter Wemm 30358f0484fSRodney W. Grimes /* 30458f0484fSRodney W. Grimes * Set up seek point for rewinddir. 30558f0484fSRodney W. Grimes */ 30658f0484fSRodney W. Grimes dirp->dd_rewind = telldir(dirp); 30755e2b2c6SBruce Evans 308adf6ad9eSPeter Wemm return (dirp); 30903dcee8dSBruce Evans 31003dcee8dSBruce Evans fail: 31103dcee8dSBruce Evans saved_errno = errno; 31203dcee8dSBruce Evans free(dirp); 31303dcee8dSBruce Evans errno = saved_errno; 31403dcee8dSBruce Evans return (NULL); 31558f0484fSRodney W. Grimes } 316