130bca6adSpendry /*
230bca6adSpendry  * Copyright (c) 1983, 1993, 1994
330bca6adSpendry  *	The Regents of the University of California.  All rights reserved.
430bca6adSpendry  *
530bca6adSpendry  * %sccs.include.redist.c%
630bca6adSpendry  */
730bca6adSpendry 
830bca6adSpendry #if defined(LIBC_SCCS) && !defined(lint)
930bca6adSpendry static char orig_sccsid[] = "@(#)opendir.c	8.2 (Berkeley) 2/12/94";
10*5c27cfefSpendry static char sccsid[] = "@(#)libc.opendir.c	8.2 (Berkeley) 06/15/94";
1130bca6adSpendry #endif /* LIBC_SCCS and not lint */
1230bca6adSpendry 
1330bca6adSpendry #include <sys/param.h>
1430bca6adSpendry #include <sys/mount.h>
1530bca6adSpendry 
1630bca6adSpendry #include <dirent.h>
1730bca6adSpendry #include <fcntl.h>
1830bca6adSpendry #include <stdlib.h>
1930bca6adSpendry #include <unistd.h>
2030bca6adSpendry 
2130bca6adSpendry /*
2230bca6adSpendry  * open a directory.
2330bca6adSpendry  */
2430bca6adSpendry DIR *
opendir(name)2530bca6adSpendry opendir(name)
2630bca6adSpendry 	const char *name;
2730bca6adSpendry {
2830bca6adSpendry 	DIR *dirp;
2930bca6adSpendry 	int fd;
3030bca6adSpendry 	int incr;
3130bca6adSpendry 	struct statfs sfb;
3230bca6adSpendry 
3330bca6adSpendry 	if ((fd = open(name, 0)) == -1)
3430bca6adSpendry 		return (NULL);
3530bca6adSpendry 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
3630bca6adSpendry 	    (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
3730bca6adSpendry 		close(fd);
3830bca6adSpendry 		return (NULL);
3930bca6adSpendry 	}
4030bca6adSpendry 
4130bca6adSpendry 	/*
4230bca6adSpendry 	 * If CLBYTES is an exact multiple of DIRBLKSIZ, use a CLBYTES
4330bca6adSpendry 	 * buffer that it cluster boundary aligned.
4430bca6adSpendry 	 * Hopefully this can be a big win someday by allowing page
4530bca6adSpendry 	 * trades trade to user space to be done by getdirentries()
4630bca6adSpendry 	 */
4730bca6adSpendry 	if ((CLBYTES % DIRBLKSIZ) == 0)
4830bca6adSpendry 		incr = CLBYTES;
4930bca6adSpendry 	else
5030bca6adSpendry 		incr = DIRBLKSIZ;
5130bca6adSpendry 
5230bca6adSpendry #ifdef MOUNT_UNION
5330bca6adSpendry 	/*
5430bca6adSpendry 	 * Determine whether this directory is the top of a union stack.
5530bca6adSpendry 	 */
5630bca6adSpendry 	if (fstatfs(fd, &sfb) < 0) {
5730bca6adSpendry 		free(dirp);
5830bca6adSpendry 		close(fd);
5930bca6adSpendry 		return (NULL);
6030bca6adSpendry 	}
6130bca6adSpendry 
6230bca6adSpendry 	if (sfb.f_type == MOUNT_UNION) {
6330bca6adSpendry 		int len = 0;
6430bca6adSpendry 		int space = 0;
6530bca6adSpendry 		char *buf = 0;
6630bca6adSpendry 		char *ddptr = 0;
6730bca6adSpendry 		int n;
6830bca6adSpendry 		struct dirent **dpv;
6930bca6adSpendry 
7030bca6adSpendry 		/*
7130bca6adSpendry 		 * The strategy here is to read all the directory
7230bca6adSpendry 		 * entries into a buffer, sort the buffer, and
7330bca6adSpendry 		 * remove duplicate entries by setting the inode
7430bca6adSpendry 		 * number to zero.
7530bca6adSpendry 		 */
7630bca6adSpendry 
7730bca6adSpendry 		/*
7830bca6adSpendry 		 * Fixup dd_loc to be non-zero to fake out readdir
7930bca6adSpendry 		 */
8030bca6adSpendry 		dirp->dd_loc = sizeof(void *);
8130bca6adSpendry 
8230bca6adSpendry 		do {
8330bca6adSpendry 			/*
8430bca6adSpendry 			 * Always make at least DIRBLKSIZ bytes
8530bca6adSpendry 			 * available to getdirentries
8630bca6adSpendry 			 */
8730bca6adSpendry 			if (space < DIRBLKSIZ) {
8830bca6adSpendry 				space += incr;
8930bca6adSpendry 				len += incr;
9030bca6adSpendry 				buf = realloc(buf, len);
9130bca6adSpendry 				if (buf == NULL) {
9230bca6adSpendry 					free(dirp);
9330bca6adSpendry 					close(fd);
9430bca6adSpendry 					return (NULL);
9530bca6adSpendry 				}
9630bca6adSpendry 				ddptr = buf + (len - space) + dirp->dd_loc;
9730bca6adSpendry 			}
9830bca6adSpendry 
9930bca6adSpendry 			n = getdirentries(fd, ddptr, space, &dirp->dd_seek);
10030bca6adSpendry 			if (n > 0) {
10130bca6adSpendry 				ddptr += n;
10230bca6adSpendry 				space -= n;
10330bca6adSpendry 			}
10430bca6adSpendry 		} while (n > 0);
10530bca6adSpendry 
10630bca6adSpendry 		/*
10730bca6adSpendry 		 * There is now a buffer full of (possibly) duplicate
10830bca6adSpendry 		 * names.
10930bca6adSpendry 		 */
11030bca6adSpendry 		dirp->dd_buf = buf;
11130bca6adSpendry 
11230bca6adSpendry 		/*
11330bca6adSpendry 		 * Go round this loop twice...
11430bca6adSpendry 		 *
11530bca6adSpendry 		 * Scan through the buffer, counting entries.
11630bca6adSpendry 		 * On the second pass, save pointers to each one.
11730bca6adSpendry 		 * Then sort the pointers and remove duplicate names.
11830bca6adSpendry 		 */
11930bca6adSpendry 		for (dpv = 0;;) {
12030bca6adSpendry 			n = 0;
12130bca6adSpendry 			ddptr = buf + dirp->dd_loc;
12230bca6adSpendry 			while (ddptr < buf + len) {
12330bca6adSpendry 				struct dirent *dp;
12430bca6adSpendry 
12530bca6adSpendry 				dp = (struct dirent *) ddptr;
12630bca6adSpendry 				if ((int)dp & 03)
12730bca6adSpendry 					break;
12830bca6adSpendry 				if ((dp->d_reclen <= 0) ||
12930bca6adSpendry 				    (dp->d_reclen > (buf + len + 1 - ddptr)))
13030bca6adSpendry 					break;
13130bca6adSpendry 				ddptr += dp->d_reclen;
13230bca6adSpendry 				if (dp->d_fileno) {
13330bca6adSpendry 					if (dpv)
13430bca6adSpendry 						dpv[n] = dp;
13530bca6adSpendry 					n++;
13630bca6adSpendry 				}
13730bca6adSpendry 			}
13830bca6adSpendry 
13930bca6adSpendry 			if (dpv) {
14030bca6adSpendry 				struct dirent *xp;
14130bca6adSpendry 
14230bca6adSpendry 				/*
143*5c27cfefSpendry 				 * This sort must be stable.
14430bca6adSpendry 				 */
145*5c27cfefSpendry 				mergesort(dpv, n, sizeof(*dpv), alphasort);
14630bca6adSpendry 
14730bca6adSpendry 				dpv[n] = NULL;
14830bca6adSpendry 				xp = NULL;
14930bca6adSpendry 
15030bca6adSpendry 				/*
15130bca6adSpendry 				 * Scan through the buffer in sort order,
15230bca6adSpendry 				 * zapping the inode number of any
15330bca6adSpendry 				 * duplicate names.
15430bca6adSpendry 				 */
15530bca6adSpendry 				for (n = 0; dpv[n]; n++) {
15630bca6adSpendry 					struct dirent *dp = dpv[n];
15730bca6adSpendry 
15830bca6adSpendry 					if ((xp == NULL) ||
15930bca6adSpendry 					    strcmp(dp->d_name, xp->d_name))
16030bca6adSpendry 						xp = dp;
16130bca6adSpendry 					else
16230bca6adSpendry 						dp->d_fileno = 0;
16330bca6adSpendry 				}
16430bca6adSpendry 
16530bca6adSpendry 				free(dpv);
16630bca6adSpendry 				break;
16730bca6adSpendry 			} else {
16830bca6adSpendry 				dpv = malloc((n+1) * sizeof(struct dirent *));
16930bca6adSpendry 				if (dpv == NULL)
17030bca6adSpendry 					break;
17130bca6adSpendry 			}
17230bca6adSpendry 		}
17330bca6adSpendry 
17430bca6adSpendry 		dirp->dd_len = len;
17530bca6adSpendry 		dirp->dd_size = ddptr - dirp->dd_buf;
17630bca6adSpendry 	} else
17730bca6adSpendry #endif /* MOUNT_UNION */
17830bca6adSpendry 	{
17930bca6adSpendry 		dirp->dd_len = incr;
18030bca6adSpendry 		dirp->dd_buf = malloc(dirp->dd_len);
18130bca6adSpendry 		if (dirp->dd_buf == NULL) {
18230bca6adSpendry 			free(dirp);
18330bca6adSpendry 			close (fd);
18430bca6adSpendry 			return (NULL);
18530bca6adSpendry 		}
18630bca6adSpendry 		dirp->dd_seek = 0;
18730bca6adSpendry 		dirp->dd_loc = 0;
18830bca6adSpendry 	}
18930bca6adSpendry 
19030bca6adSpendry 	dirp->dd_fd = fd;
19130bca6adSpendry 
19230bca6adSpendry 	/*
19330bca6adSpendry 	 * Set up seek point for rewinddir.
19430bca6adSpendry 	 */
19530bca6adSpendry 	dirp->dd_rewind = telldir(dirp);
19630bca6adSpendry 
19730bca6adSpendry 	return (dirp);
19830bca6adSpendry }
199