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.4 (Berkeley) 06/15/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 DIR *dirp; 28 int fd; 29 int incr; 30 struct statfs sfb; 31 32 if ((fd = open(name, 0)) == -1) 33 return (NULL); 34 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || 35 (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { 36 close(fd); 37 return (NULL); 38 } 39 40 /* 41 * If CLBYTES is an exact multiple of DIRBLKSIZ, use a CLBYTES 42 * buffer that it cluster boundary aligned. 43 * Hopefully this can be a big win someday by allowing page 44 * trades to user space to be done by getdirentries() 45 */ 46 if ((CLBYTES % DIRBLKSIZ) == 0) 47 incr = CLBYTES; 48 else 49 incr = DIRBLKSIZ; 50 51 #ifdef MOUNT_UNION 52 /* 53 * Determine whether this directory is the top of a union stack. 54 */ 55 if (fstatfs(fd, &sfb) < 0) { 56 free(dirp); 57 close(fd); 58 return (NULL); 59 } 60 61 if (sfb.f_type == MOUNT_UNION) { 62 int len = 0; 63 int space = 0; 64 char *buf = 0; 65 char *ddptr = 0; 66 int n; 67 struct dirent **dpv; 68 69 /* 70 * The strategy here is to read all the directory 71 * entries into a buffer, sort the buffer, and 72 * remove duplicate entries by setting the inode 73 * number to zero. 74 */ 75 76 /* 77 * Fixup dd_loc to be non-zero to fake out readdir 78 */ 79 dirp->dd_loc = sizeof(void *); 80 81 do { 82 /* 83 * Always make at least DIRBLKSIZ bytes 84 * available to getdirentries 85 */ 86 if (space < DIRBLKSIZ) { 87 space += incr; 88 len += incr; 89 buf = realloc(buf, len); 90 if (buf == NULL) { 91 free(dirp); 92 close(fd); 93 return (NULL); 94 } 95 ddptr = buf + (len - space) + dirp->dd_loc; 96 } 97 98 n = getdirentries(fd, ddptr, space, &dirp->dd_seek); 99 if (n > 0) { 100 ddptr += n; 101 space -= n; 102 } 103 } while (n > 0); 104 105 /* 106 * There is now a buffer full of (possibly) duplicate 107 * names. 108 */ 109 dirp->dd_buf = buf; 110 111 /* 112 * Go round this loop twice... 113 * 114 * Scan through the buffer, counting entries. 115 * On the second pass, save pointers to each one. 116 * Then sort the pointers and remove duplicate names. 117 */ 118 for (dpv = 0;;) { 119 n = 0; 120 ddptr = buf + dirp->dd_loc; 121 while (ddptr < buf + len) { 122 struct dirent *dp; 123 124 dp = (struct dirent *) ddptr; 125 if ((int)dp & 03) 126 break; 127 if ((dp->d_reclen <= 0) || 128 (dp->d_reclen > (buf + len + 1 - ddptr))) 129 break; 130 ddptr += dp->d_reclen; 131 if (dp->d_fileno) { 132 if (dpv) 133 dpv[n] = dp; 134 n++; 135 } 136 } 137 138 if (dpv) { 139 struct dirent *xp; 140 141 /* 142 * This sort must be stable. 143 */ 144 mergesort(dpv, n, sizeof(*dpv), alphasort); 145 146 dpv[n] = NULL; 147 xp = NULL; 148 149 /* 150 * Scan through the buffer in sort order, 151 * zapping the inode number of any 152 * duplicate names. 153 */ 154 for (n = 0; dpv[n]; n++) { 155 struct dirent *dp = dpv[n]; 156 157 if ((xp == NULL) || 158 strcmp(dp->d_name, xp->d_name)) 159 xp = dp; 160 else 161 dp->d_fileno = 0; 162 } 163 164 free(dpv); 165 break; 166 } else { 167 dpv = malloc((n+1) * sizeof(struct dirent *)); 168 if (dpv == NULL) 169 break; 170 } 171 } 172 173 dirp->dd_len = len; 174 dirp->dd_size = ddptr - dirp->dd_buf; 175 } else 176 #endif /* MOUNT_UNION */ 177 { 178 dirp->dd_len = incr; 179 dirp->dd_buf = malloc(dirp->dd_len); 180 if (dirp->dd_buf == NULL) { 181 free(dirp); 182 close (fd); 183 return (NULL); 184 } 185 dirp->dd_seek = 0; 186 dirp->dd_loc = 0; 187 } 188 189 dirp->dd_fd = fd; 190 191 /* 192 * Set up seek point for rewinddir. 193 */ 194 dirp->dd_rewind = telldir(dirp); 195 196 return (dirp); 197 } 198