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.6 (Berkeley) 08/14/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 int n; 82 struct dirent **dpv; 83 84 /* 85 * The strategy here is to read all the directory 86 * entries into a buffer, sort the buffer, and 87 * remove duplicate entries by setting the inode 88 * number to zero. 89 */ 90 91 do { 92 /* 93 * Always make at least DIRBLKSIZ bytes 94 * available to getdirentries 95 */ 96 if (space < DIRBLKSIZ) { 97 space += incr; 98 len += incr; 99 buf = realloc(buf, len); 100 if (buf == NULL) { 101 free(dirp); 102 close(fd); 103 return (NULL); 104 } 105 ddptr = buf + (len - space); 106 } 107 108 n = getdirentries(fd, ddptr, space, &dirp->dd_seek); 109 if (n > 0) { 110 ddptr += n; 111 space -= n; 112 } 113 } while (n > 0); 114 115 flags |= __DTF_READALL; 116 117 /* 118 * Re-open the directory. 119 * This has the effect of rewinding back to the 120 * top of the union stack and is needed by 121 * programs which plan to fchdir to a descriptor 122 * which has also been read -- see fts.c. 123 */ 124 if (flags & DTF_REWIND) { 125 (void) close(fd); 126 if ((fd = open(name, O_RDONLY)) == -1) { 127 free(buf); 128 free(dirp); 129 return (NULL); 130 } 131 } 132 133 /* 134 * There is now a buffer full of (possibly) duplicate 135 * names. 136 */ 137 dirp->dd_buf = buf; 138 139 /* 140 * Go round this loop twice... 141 * 142 * Scan through the buffer, counting entries. 143 * On the second pass, save pointers to each one. 144 * Then sort the pointers and remove duplicate names. 145 */ 146 for (dpv = 0;;) { 147 n = 0; 148 ddptr = buf; 149 while (ddptr < buf + len) { 150 struct dirent *dp; 151 152 dp = (struct dirent *) ddptr; 153 if ((int)dp & 03) 154 break; 155 if ((dp->d_reclen <= 0) || 156 (dp->d_reclen > (buf + len + 1 - ddptr))) 157 break; 158 ddptr += dp->d_reclen; 159 if (dp->d_fileno) { 160 if (dpv) 161 dpv[n] = dp; 162 n++; 163 } 164 } 165 166 if (dpv) { 167 struct dirent *xp; 168 169 /* 170 * This sort must be stable. 171 */ 172 mergesort(dpv, n, sizeof(*dpv), alphasort); 173 174 dpv[n] = NULL; 175 xp = NULL; 176 177 /* 178 * Scan through the buffer in sort order, 179 * zapping the inode number of any 180 * duplicate names. 181 */ 182 for (n = 0; dpv[n]; n++) { 183 struct dirent *dp = dpv[n]; 184 185 if ((xp == NULL) || 186 strcmp(dp->d_name, xp->d_name)) { 187 xp = dp; 188 } else { 189 dp->d_fileno = 0; 190 } 191 if (dp->d_type == DT_WHT && 192 (flags & DTF_HIDEW)) 193 dp->d_fileno = 0; 194 } 195 196 free(dpv); 197 break; 198 } else { 199 dpv = malloc((n+1) * sizeof(struct dirent *)); 200 if (dpv == NULL) 201 break; 202 } 203 } 204 205 dirp->dd_len = len; 206 dirp->dd_size = ddptr - dirp->dd_buf; 207 } else { 208 dirp->dd_len = incr; 209 dirp->dd_buf = malloc(dirp->dd_len); 210 if (dirp->dd_buf == NULL) { 211 free(dirp); 212 close (fd); 213 return (NULL); 214 } 215 dirp->dd_seek = 0; 216 flags &= ~DTF_REWIND; 217 } 218 219 dirp->dd_loc = 0; 220 dirp->dd_fd = fd; 221 dirp->dd_flags = flags; 222 223 /* 224 * Set up seek point for rewinddir. 225 */ 226 dirp->dd_rewind = telldir(dirp); 227 228 return (dirp); 229 } 230