1 /* $NetBSD: opendir.c,v 1.12 1997/02/25 13:16:39 fvdl Exp $ */ 2 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #if defined(LIBC_SCCS) && !defined(lint) 37 #if 0 38 static char sccsid[] = "@(#)opendir.c 8.7 (Berkeley) 12/10/94"; 39 #else 40 static char rcsid[] = "$NetBSD: opendir.c,v 1.12 1997/02/25 13:16:39 fvdl Exp $"; 41 #endif 42 #endif /* LIBC_SCCS and not lint */ 43 44 #include <sys/param.h> 45 #include <sys/mount.h> 46 #include <sys/stat.h> 47 48 #include <dirent.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 55 /* 56 * Open a directory. 57 */ 58 DIR * 59 opendir(name) 60 const char *name; 61 { 62 63 return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 64 } 65 66 DIR * 67 __opendir2(name, flags) 68 const char *name; 69 int flags; 70 { 71 DIR *dirp; 72 int fd; 73 struct stat sb; 74 int pagesz; 75 int incr; 76 int unionstack; 77 78 if ((fd = open(name, O_RDONLY | O_NONBLOCK)) == -1) 79 return (NULL); 80 if (fstat(fd, &sb) || !S_ISDIR(sb.st_mode)) { 81 errno = ENOTDIR; 82 close(fd); 83 return (NULL); 84 } 85 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 86 (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { 87 close(fd); 88 return (NULL); 89 } 90 91 /* 92 * If the machine's page size is an exact multiple of DIRBLKSIZ, 93 * use a buffer that is cluster boundary aligned. 94 * Hopefully this can be a big win someday by allowing page trades 95 * to user space to be done by getdirentries() 96 */ 97 if (((pagesz = getpagesize()) % DIRBLKSIZ) == 0) 98 incr = pagesz; 99 else 100 incr = DIRBLKSIZ; 101 102 /* 103 * Determine whether this directory is the top of a union stack. 104 */ 105 if (flags & DTF_NODUP) { 106 struct statfs sfb; 107 108 if (fstatfs(fd, &sfb) < 0) { 109 free(dirp); 110 close(fd); 111 return (NULL); 112 } 113 unionstack = !(strncmp(sfb.f_fstypename, MOUNT_UNION, 114 MFSNAMELEN)) || (sfb.f_flags & MNT_UNION); 115 } else { 116 unionstack = 0; 117 } 118 119 if (unionstack) { 120 int len = 0; 121 int space = 0; 122 char *buf = 0; 123 char *ddptr = 0; 124 char *ddeptr; 125 int n; 126 struct dirent **dpv; 127 128 /* 129 * The strategy here is to read all the directory 130 * entries into a buffer, sort the buffer, and 131 * remove duplicate entries by setting the inode 132 * number to zero. 133 */ 134 135 do { 136 /* 137 * Always make at least DIRBLKSIZ bytes 138 * available to getdirentries 139 */ 140 if (space < DIRBLKSIZ) { 141 space += incr; 142 len += incr; 143 buf = realloc(buf, len); 144 if (buf == NULL) { 145 free(dirp); 146 close(fd); 147 return (NULL); 148 } 149 ddptr = buf + (len - space); 150 } 151 152 n = getdirentries(fd, ddptr, space, &dirp->dd_seek); 153 if (n > 0) { 154 ddptr += n; 155 space -= n; 156 } 157 } while (n > 0); 158 159 ddeptr = ddptr; 160 flags |= __DTF_READALL; 161 162 /* 163 * Re-open the directory. 164 * This has the effect of rewinding back to the 165 * top of the union stack and is needed by 166 * programs which plan to fchdir to a descriptor 167 * which has also been read -- see fts.c. 168 */ 169 if (flags & DTF_REWIND) { 170 (void) close(fd); 171 if ((fd = open(name, O_RDONLY)) == -1) { 172 free(buf); 173 free(dirp); 174 return (NULL); 175 } 176 } 177 178 /* 179 * There is now a buffer full of (possibly) duplicate 180 * names. 181 */ 182 dirp->dd_buf = buf; 183 184 /* 185 * Go round this loop twice... 186 * 187 * Scan through the buffer, counting entries. 188 * On the second pass, save pointers to each one. 189 * Then sort the pointers and remove duplicate names. 190 */ 191 for (dpv = 0;;) { 192 for (n = 0, ddptr = buf; ddptr < ddeptr;) { 193 struct dirent *dp; 194 195 dp = (struct dirent *) ddptr; 196 if ((long)dp & 03) 197 break; 198 if ((dp->d_reclen <= 0) || 199 (dp->d_reclen > (ddeptr + 1 - ddptr))) 200 break; 201 ddptr += dp->d_reclen; 202 if (dp->d_fileno) { 203 if (dpv) 204 dpv[n] = dp; 205 n++; 206 } 207 } 208 209 if (dpv) { 210 struct dirent *xp; 211 212 /* 213 * This sort must be stable. 214 */ 215 mergesort(dpv, n, sizeof(*dpv), alphasort); 216 217 dpv[n] = NULL; 218 xp = NULL; 219 220 /* 221 * Scan through the buffer in sort order, 222 * zapping the inode number of any 223 * duplicate names. 224 */ 225 for (n = 0; dpv[n]; n++) { 226 struct dirent *dp = dpv[n]; 227 228 if ((xp == NULL) || 229 strcmp(dp->d_name, xp->d_name)) 230 xp = dp; 231 else 232 dp->d_fileno = 0; 233 if (dp->d_type == DT_WHT && 234 (flags & DTF_HIDEW)) 235 dp->d_fileno = 0; 236 } 237 238 free(dpv); 239 break; 240 } else { 241 dpv = malloc((n+1) * sizeof(struct dirent *)); 242 if (dpv == NULL) 243 break; 244 } 245 } 246 247 dirp->dd_len = len; 248 dirp->dd_size = ddptr - dirp->dd_buf; 249 } else { 250 dirp->dd_len = incr; 251 dirp->dd_buf = malloc(dirp->dd_len); 252 if (dirp->dd_buf == NULL) { 253 free(dirp); 254 close (fd); 255 return (NULL); 256 } 257 dirp->dd_seek = 0; 258 flags &= ~DTF_REWIND; 259 } 260 261 dirp->dd_loc = 0; 262 dirp->dd_fd = fd; 263 dirp->dd_flags = flags; 264 265 /* 266 * Set up seek point for rewinddir. 267 */ 268 dirp->dd_rewind = telldir(dirp); 269 270 return (dirp); 271 } 272