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