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