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