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