1 /* 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #if defined(LIBC_SCCS) && !defined(lint) 9 static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 05/01/95"; 10 #endif /* LIBC_SCCS and not lint */ 11 12 #include <sys/param.h> 13 #include <sys/mount.h> 14 #include <sys/stat.h> 15 16 #include <dirent.h> 17 #include <errno.h> 18 #include <fcntl.h> 19 #include <stdlib.h> 20 #include <unistd.h> 21 22 /* 23 * Open a directory. 24 */ 25 DIR * 26 opendir(name) 27 const char *name; 28 { 29 30 return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); 31 } 32 33 DIR * 34 __opendir2(name, flags) 35 const char *name; 36 int flags; 37 { 38 DIR *dirp; 39 int fd; 40 int incr; 41 int unionstack; 42 struct stat statb; 43 44 if ((fd = open(name, O_RDONLY)) == -1) 45 return (NULL); 46 if (fstat(fd, &statb) || !S_ISDIR(statb.st_mode)) { 47 errno = ENOTDIR; 48 close(fd); 49 return (NULL); 50 } 51 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 52 (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { 53 close(fd); 54 return (NULL); 55 } 56 57 /* 58 * If CLBYTES is an exact multiple of DIRBLKSIZ, use a CLBYTES 59 * buffer that it cluster boundary aligned. 60 * Hopefully this can be a big win someday by allowing page 61 * trades to user space to be done by getdirentries() 62 */ 63 if ((CLBYTES % DIRBLKSIZ) == 0) 64 incr = CLBYTES; 65 else 66 incr = DIRBLKSIZ; 67 68 /* 69 * Determine whether this directory is the top of a union stack. 70 */ 71 if (flags & DTF_NODUP) { 72 struct statfs sfb; 73 74 if (fstatfs(fd, &sfb) < 0) { 75 free(dirp); 76 close(fd); 77 return (NULL); 78 } 79 unionstack = !strcmp(sfb.f_fstypename, "union"); 80 } else { 81 unionstack = 0; 82 } 83 84 if (unionstack) { 85 int len = 0; 86 int space = 0; 87 char *buf = 0; 88 char *ddptr = 0; 89 char *ddeptr; 90 int n; 91 struct dirent **dpv; 92 93 /* 94 * The strategy here is to read all the directory 95 * entries into a buffer, sort the buffer, and 96 * remove duplicate entries by setting the inode 97 * number to zero. 98 */ 99 100 do { 101 /* 102 * Always make at least DIRBLKSIZ bytes 103 * available to getdirentries 104 */ 105 if (space < DIRBLKSIZ) { 106 space += incr; 107 len += incr; 108 buf = realloc(buf, len); 109 if (buf == NULL) { 110 free(dirp); 111 close(fd); 112 return (NULL); 113 } 114 ddptr = buf + (len - space); 115 } 116 117 n = getdirentries(fd, ddptr, space, &dirp->dd_seek); 118 if (n > 0) { 119 ddptr += n; 120 space -= n; 121 } 122 } while (n > 0); 123 124 ddeptr = ddptr; 125 flags |= __DTF_READALL; 126 127 /* 128 * Re-open the directory. 129 * This has the effect of rewinding back to the 130 * top of the union stack and is needed by 131 * programs which plan to fchdir to a descriptor 132 * which has also been read -- see fts.c. 133 */ 134 if (flags & DTF_REWIND) { 135 (void) close(fd); 136 if ((fd = open(name, O_RDONLY)) == -1) { 137 free(buf); 138 free(dirp); 139 return (NULL); 140 } 141 } 142 143 /* 144 * There is now a buffer full of (possibly) duplicate 145 * names. 146 */ 147 dirp->dd_buf = buf; 148 149 /* 150 * Go round this loop twice... 151 * 152 * Scan through the buffer, counting entries. 153 * On the second pass, save pointers to each one. 154 * Then sort the pointers and remove duplicate names. 155 */ 156 for (dpv = 0;;) { 157 n = 0; 158 ddptr = buf; 159 while (ddptr < ddeptr) { 160 struct dirent *dp; 161 162 dp = (struct dirent *) ddptr; 163 if ((int)dp & 03) 164 break; 165 if ((dp->d_reclen <= 0) || 166 (dp->d_reclen > (ddeptr + 1 - ddptr))) 167 break; 168 ddptr += dp->d_reclen; 169 if (dp->d_fileno) { 170 if (dpv) 171 dpv[n] = dp; 172 n++; 173 } 174 } 175 176 if (dpv) { 177 struct dirent *xp; 178 179 /* 180 * This sort must be stable. 181 */ 182 mergesort(dpv, n, sizeof(*dpv), alphasort); 183 184 dpv[n] = NULL; 185 xp = NULL; 186 187 /* 188 * Scan through the buffer in sort order, 189 * zapping the inode number of any 190 * duplicate names. 191 */ 192 for (n = 0; dpv[n]; n++) { 193 struct dirent *dp = dpv[n]; 194 195 if ((xp == NULL) || 196 strcmp(dp->d_name, xp->d_name)) { 197 xp = dp; 198 } else { 199 dp->d_fileno = 0; 200 } 201 if (dp->d_type == DT_WHT && 202 (flags & DTF_HIDEW)) 203 dp->d_fileno = 0; 204 } 205 206 free(dpv); 207 break; 208 } else { 209 dpv = malloc((n+1) * sizeof(struct dirent *)); 210 if (dpv == NULL) 211 break; 212 } 213 } 214 215 dirp->dd_len = len; 216 dirp->dd_size = ddptr - dirp->dd_buf; 217 } else { 218 dirp->dd_len = incr; 219 dirp->dd_buf = malloc(dirp->dd_len); 220 if (dirp->dd_buf == NULL) { 221 free(dirp); 222 close (fd); 223 return (NULL); 224 } 225 dirp->dd_seek = 0; 226 flags &= ~DTF_REWIND; 227 } 228 229 dirp->dd_loc = 0; 230 dirp->dd_fd = fd; 231 dirp->dd_flags = flags; 232 233 /* 234 * Set up seek point for rewinddir. 235 */ 236 dirp->dd_rewind = telldir(dirp); 237 238 return (dirp); 239 } 240