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