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