166093Spendry /*
266093Spendry  * Copyright (c) 1983, 1993, 1994
366093Spendry  *	The Regents of the University of California.  All rights reserved.
466093Spendry  *
566093Spendry  * %sccs.include.redist.c%
666093Spendry  */
766093Spendry 
866093Spendry #if defined(LIBC_SCCS) && !defined(lint)
966093Spendry static char orig_sccsid[] = "@(#)opendir.c	8.2 (Berkeley) 2/12/94";
10*67391Spendry static char sccsid[] = "@(#)libc.opendir.c	8.2 (Berkeley) 06/15/94";
1166093Spendry #endif /* LIBC_SCCS and not lint */
1266093Spendry 
1366093Spendry #include <sys/param.h>
1466093Spendry #include <sys/mount.h>
1566093Spendry 
1666093Spendry #include <dirent.h>
1766093Spendry #include <fcntl.h>
1866093Spendry #include <stdlib.h>
1966093Spendry #include <unistd.h>
2066093Spendry 
2166093Spendry /*
2266093Spendry  * open a directory.
2366093Spendry  */
2466093Spendry DIR *
opendir(name)2566093Spendry opendir(name)
2666093Spendry 	const char *name;
2766093Spendry {
2866093Spendry 	DIR *dirp;
2966093Spendry 	int fd;
3066093Spendry 	int incr;
3166093Spendry 	struct statfs sfb;
3266093Spendry 
3366093Spendry 	if ((fd = open(name, 0)) == -1)
3466093Spendry 		return (NULL);
3566093Spendry 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
3666093Spendry 	    (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
3766093Spendry 		close(fd);
3866093Spendry 		return (NULL);
3966093Spendry 	}
4066093Spendry 
4166093Spendry 	/*
4266093Spendry 	 * If CLBYTES is an exact multiple of DIRBLKSIZ, use a CLBYTES
4366093Spendry 	 * buffer that it cluster boundary aligned.
4466093Spendry 	 * Hopefully this can be a big win someday by allowing page
4566093Spendry 	 * trades trade to user space to be done by getdirentries()
4666093Spendry 	 */
4766093Spendry 	if ((CLBYTES % DIRBLKSIZ) == 0)
4866093Spendry 		incr = CLBYTES;
4966093Spendry 	else
5066093Spendry 		incr = DIRBLKSIZ;
5166093Spendry 
5266093Spendry #ifdef MOUNT_UNION
5366093Spendry 	/*
5466093Spendry 	 * Determine whether this directory is the top of a union stack.
5566093Spendry 	 */
5666093Spendry 	if (fstatfs(fd, &sfb) < 0) {
5766093Spendry 		free(dirp);
5866093Spendry 		close(fd);
5966093Spendry 		return (NULL);
6066093Spendry 	}
6166093Spendry 
6266093Spendry 	if (sfb.f_type == MOUNT_UNION) {
6366093Spendry 		int len = 0;
6466093Spendry 		int space = 0;
6566093Spendry 		char *buf = 0;
6666093Spendry 		char *ddptr = 0;
6766093Spendry 		int n;
6866093Spendry 		struct dirent **dpv;
6966093Spendry 
7066093Spendry 		/*
7166093Spendry 		 * The strategy here is to read all the directory
7266093Spendry 		 * entries into a buffer, sort the buffer, and
7366093Spendry 		 * remove duplicate entries by setting the inode
7466093Spendry 		 * number to zero.
7566093Spendry 		 */
7666093Spendry 
7766093Spendry 		/*
7866093Spendry 		 * Fixup dd_loc to be non-zero to fake out readdir
7966093Spendry 		 */
8066093Spendry 		dirp->dd_loc = sizeof(void *);
8166093Spendry 
8266093Spendry 		do {
8366093Spendry 			/*
8466093Spendry 			 * Always make at least DIRBLKSIZ bytes
8566093Spendry 			 * available to getdirentries
8666093Spendry 			 */
8766093Spendry 			if (space < DIRBLKSIZ) {
8866093Spendry 				space += incr;
8966093Spendry 				len += incr;
9066093Spendry 				buf = realloc(buf, len);
9166093Spendry 				if (buf == NULL) {
9266093Spendry 					free(dirp);
9366093Spendry 					close(fd);
9466093Spendry 					return (NULL);
9566093Spendry 				}
9666093Spendry 				ddptr = buf + (len - space) + dirp->dd_loc;
9766093Spendry 			}
9866093Spendry 
9966093Spendry 			n = getdirentries(fd, ddptr, space, &dirp->dd_seek);
10066093Spendry 			if (n > 0) {
10166093Spendry 				ddptr += n;
10266093Spendry 				space -= n;
10366093Spendry 			}
10466093Spendry 		} while (n > 0);
10566093Spendry 
10666093Spendry 		/*
10766093Spendry 		 * There is now a buffer full of (possibly) duplicate
10866093Spendry 		 * names.
10966093Spendry 		 */
11066093Spendry 		dirp->dd_buf = buf;
11166093Spendry 
11266093Spendry 		/*
11366093Spendry 		 * Go round this loop twice...
11466093Spendry 		 *
11566093Spendry 		 * Scan through the buffer, counting entries.
11666093Spendry 		 * On the second pass, save pointers to each one.
11766093Spendry 		 * Then sort the pointers and remove duplicate names.
11866093Spendry 		 */
11966093Spendry 		for (dpv = 0;;) {
12066093Spendry 			n = 0;
12166093Spendry 			ddptr = buf + dirp->dd_loc;
12266093Spendry 			while (ddptr < buf + len) {
12366093Spendry 				struct dirent *dp;
12466093Spendry 
12566093Spendry 				dp = (struct dirent *) ddptr;
12666093Spendry 				if ((int)dp & 03)
12766093Spendry 					break;
12866093Spendry 				if ((dp->d_reclen <= 0) ||
12966093Spendry 				    (dp->d_reclen > (buf + len + 1 - ddptr)))
13066093Spendry 					break;
13166093Spendry 				ddptr += dp->d_reclen;
13266093Spendry 				if (dp->d_fileno) {
13366093Spendry 					if (dpv)
13466093Spendry 						dpv[n] = dp;
13566093Spendry 					n++;
13666093Spendry 				}
13766093Spendry 			}
13866093Spendry 
13966093Spendry 			if (dpv) {
14066093Spendry 				struct dirent *xp;
14166093Spendry 
14266093Spendry 				/*
143*67391Spendry 				 * This sort must be stable.
14466093Spendry 				 */
145*67391Spendry 				mergesort(dpv, n, sizeof(*dpv), alphasort);
14666093Spendry 
14766093Spendry 				dpv[n] = NULL;
14866093Spendry 				xp = NULL;
14966093Spendry 
15066093Spendry 				/*
15166093Spendry 				 * Scan through the buffer in sort order,
15266093Spendry 				 * zapping the inode number of any
15366093Spendry 				 * duplicate names.
15466093Spendry 				 */
15566093Spendry 				for (n = 0; dpv[n]; n++) {
15666093Spendry 					struct dirent *dp = dpv[n];
15766093Spendry 
15866093Spendry 					if ((xp == NULL) ||
15966093Spendry 					    strcmp(dp->d_name, xp->d_name))
16066093Spendry 						xp = dp;
16166093Spendry 					else
16266093Spendry 						dp->d_fileno = 0;
16366093Spendry 				}
16466093Spendry 
16566093Spendry 				free(dpv);
16666093Spendry 				break;
16766093Spendry 			} else {
16866093Spendry 				dpv = malloc((n+1) * sizeof(struct dirent *));
16966093Spendry 				if (dpv == NULL)
17066093Spendry 					break;
17166093Spendry 			}
17266093Spendry 		}
17366093Spendry 
17466093Spendry 		dirp->dd_len = len;
17566093Spendry 		dirp->dd_size = ddptr - dirp->dd_buf;
17666093Spendry 	} else
17766093Spendry #endif /* MOUNT_UNION */
17866093Spendry 	{
17966093Spendry 		dirp->dd_len = incr;
18066093Spendry 		dirp->dd_buf = malloc(dirp->dd_len);
18166093Spendry 		if (dirp->dd_buf == NULL) {
18266093Spendry 			free(dirp);
18366093Spendry 			close (fd);
18466093Spendry 			return (NULL);
18566093Spendry 		}
18666093Spendry 		dirp->dd_seek = 0;
18766093Spendry 		dirp->dd_loc = 0;
18866093Spendry 	}
18966093Spendry 
19066093Spendry 	dirp->dd_fd = fd;
19166093Spendry 
19266093Spendry 	/*
19366093Spendry 	 * Set up seek point for rewinddir.
19466093Spendry 	 */
19566093Spendry 	dirp->dd_rewind = telldir(dirp);
19666093Spendry 
19766093Spendry 	return (dirp);
19866093Spendry }
199