121350Sdist /*
262736Sbostic * Copyright (c) 1983, 1993
362736Sbostic * The Regents of the University of California. All rights reserved.
434790Sbostic *
542625Sbostic * %sccs.include.redist.c%
621350Sdist */
721350Sdist
826573Sdonn #if defined(LIBC_SCCS) && !defined(lint)
9*69136Smckusick static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 05/01/95";
1034790Sbostic #endif /* LIBC_SCCS and not lint */
115750Smckusick
129142Smckusick #include <sys/param.h>
1367111Smckusick #include <sys/mount.h>
14*69136Smckusick #include <sys/stat.h>
1566070Sbostic
1636545Smckusick #include <dirent.h>
17*69136Smckusick #include <errno.h>
1838664Smckusick #include <fcntl.h>
1946597Sdonn #include <stdlib.h>
2046597Sdonn #include <unistd.h>
215750Smckusick
225750Smckusick /*
2367111Smckusick * Open a directory.
245750Smckusick */
255750Smckusick DIR *
opendir(name)265750Smckusick opendir(name)
2746597Sdonn const char *name;
285750Smckusick {
2967575Spendry
3067575Spendry return (__opendir2(name, DTF_HIDEW|DTF_NODUP));
3167575Spendry }
3267575Spendry
3367575Spendry DIR *
__opendir2(name,flags)3467575Spendry __opendir2(name, flags)
3567575Spendry const char *name;
3667575Spendry int flags;
3767575Spendry {
3867111Smckusick DIR *dirp;
3967111Smckusick int fd;
4067111Smckusick int incr;
4167575Spendry int unionstack;
42*69136Smckusick struct stat statb;
435750Smckusick
4467575Spendry if ((fd = open(name, O_RDONLY)) == -1)
4567111Smckusick return (NULL);
46*69136Smckusick if (fstat(fd, &statb) || !S_ISDIR(statb.st_mode)) {
47*69136Smckusick errno = ENOTDIR;
48*69136Smckusick close(fd);
49*69136Smckusick return (NULL);
50*69136Smckusick }
5157854Sralph if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
5238664Smckusick (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
5367111Smckusick close(fd);
5467111Smckusick return (NULL);
556097Smckusic }
5667111Smckusick
5738435Smckusick /*
5852940Sralph * If CLBYTES is an exact multiple of DIRBLKSIZ, use a CLBYTES
5938435Smckusick * buffer that it cluster boundary aligned.
6067111Smckusick * Hopefully this can be a big win someday by allowing page
6167111Smckusick * trades to user space to be done by getdirentries()
6238435Smckusick */
6367111Smckusick if ((CLBYTES % DIRBLKSIZ) == 0)
6467111Smckusick incr = CLBYTES;
6567111Smckusick else
6667111Smckusick incr = DIRBLKSIZ;
6767111Smckusick
6867111Smckusick /*
6967111Smckusick * Determine whether this directory is the top of a union stack.
7067111Smckusick */
7167575Spendry if (flags & DTF_NODUP) {
7267575Spendry struct statfs sfb;
7367575Spendry
7467575Spendry if (fstatfs(fd, &sfb) < 0) {
7567575Spendry free(dirp);
7667575Spendry close(fd);
7767575Spendry return (NULL);
7867575Spendry }
79*69136Smckusick unionstack = !strcmp(sfb.f_fstypename, "union");
8067575Spendry } else {
8167575Spendry unionstack = 0;
8238435Smckusick }
8367111Smckusick
8467575Spendry if (unionstack) {
8567111Smckusick int len = 0;
8667111Smckusick int space = 0;
8767111Smckusick char *buf = 0;
8867111Smckusick char *ddptr = 0;
8968077Spendry char *ddeptr;
9067111Smckusick int n;
9167111Smckusick struct dirent **dpv;
9267111Smckusick
9367111Smckusick /*
9467111Smckusick * The strategy here is to read all the directory
9567111Smckusick * entries into a buffer, sort the buffer, and
9667111Smckusick * remove duplicate entries by setting the inode
9767111Smckusick * number to zero.
9867111Smckusick */
9967111Smckusick
10067111Smckusick do {
10167111Smckusick /*
10267111Smckusick * Always make at least DIRBLKSIZ bytes
10367111Smckusick * available to getdirentries
10467111Smckusick */
10567111Smckusick if (space < DIRBLKSIZ) {
10667111Smckusick space += incr;
10767111Smckusick len += incr;
10867111Smckusick buf = realloc(buf, len);
10967111Smckusick if (buf == NULL) {
11067111Smckusick free(dirp);
11167111Smckusick close(fd);
11267111Smckusick return (NULL);
11367111Smckusick }
11467575Spendry ddptr = buf + (len - space);
11567111Smckusick }
11667111Smckusick
11767111Smckusick n = getdirentries(fd, ddptr, space, &dirp->dd_seek);
11867111Smckusick if (n > 0) {
11967111Smckusick ddptr += n;
12067111Smckusick space -= n;
12167111Smckusick }
12267111Smckusick } while (n > 0);
12367111Smckusick
12468077Spendry ddeptr = ddptr;
12567575Spendry flags |= __DTF_READALL;
12667575Spendry
12767111Smckusick /*
12867575Spendry * Re-open the directory.
12967575Spendry * This has the effect of rewinding back to the
13067575Spendry * top of the union stack and is needed by
13167575Spendry * programs which plan to fchdir to a descriptor
13267575Spendry * which has also been read -- see fts.c.
13367575Spendry */
13467575Spendry if (flags & DTF_REWIND) {
13567575Spendry (void) close(fd);
13667575Spendry if ((fd = open(name, O_RDONLY)) == -1) {
13767575Spendry free(buf);
13867575Spendry free(dirp);
13967575Spendry return (NULL);
14067575Spendry }
14167575Spendry }
14267575Spendry
14367575Spendry /*
14467111Smckusick * There is now a buffer full of (possibly) duplicate
14567111Smckusick * names.
14667111Smckusick */
14767111Smckusick dirp->dd_buf = buf;
14867111Smckusick
14967111Smckusick /*
15067111Smckusick * Go round this loop twice...
15167111Smckusick *
15267111Smckusick * Scan through the buffer, counting entries.
15367111Smckusick * On the second pass, save pointers to each one.
15467111Smckusick * Then sort the pointers and remove duplicate names.
15567111Smckusick */
15667111Smckusick for (dpv = 0;;) {
15767111Smckusick n = 0;
15867575Spendry ddptr = buf;
15968077Spendry while (ddptr < ddeptr) {
16067111Smckusick struct dirent *dp;
16167111Smckusick
16267111Smckusick dp = (struct dirent *) ddptr;
16367111Smckusick if ((int)dp & 03)
16467111Smckusick break;
16567111Smckusick if ((dp->d_reclen <= 0) ||
16668077Spendry (dp->d_reclen > (ddeptr + 1 - ddptr)))
16767111Smckusick break;
16867111Smckusick ddptr += dp->d_reclen;
16967111Smckusick if (dp->d_fileno) {
17067111Smckusick if (dpv)
17167111Smckusick dpv[n] = dp;
17267111Smckusick n++;
17367111Smckusick }
17467111Smckusick }
17567111Smckusick
17667111Smckusick if (dpv) {
17767111Smckusick struct dirent *xp;
17867111Smckusick
17967111Smckusick /*
18067386Spendry * This sort must be stable.
18167111Smckusick */
18267386Spendry mergesort(dpv, n, sizeof(*dpv), alphasort);
18367111Smckusick
18467111Smckusick dpv[n] = NULL;
18567111Smckusick xp = NULL;
18667111Smckusick
18767111Smckusick /*
18867111Smckusick * Scan through the buffer in sort order,
18967111Smckusick * zapping the inode number of any
19067111Smckusick * duplicate names.
19167111Smckusick */
19267111Smckusick for (n = 0; dpv[n]; n++) {
19367111Smckusick struct dirent *dp = dpv[n];
19467111Smckusick
19567111Smckusick if ((xp == NULL) ||
19667575Spendry strcmp(dp->d_name, xp->d_name)) {
19767111Smckusick xp = dp;
19867575Spendry } else {
19967111Smckusick dp->d_fileno = 0;
20067575Spendry }
20167676Smckusick if (dp->d_type == DT_WHT &&
20267575Spendry (flags & DTF_HIDEW))
20367575Spendry dp->d_fileno = 0;
20467111Smckusick }
20567111Smckusick
20667111Smckusick free(dpv);
20767111Smckusick break;
20867111Smckusick } else {
20967111Smckusick dpv = malloc((n+1) * sizeof(struct dirent *));
21067111Smckusick if (dpv == NULL)
21167111Smckusick break;
21267111Smckusick }
21367111Smckusick }
21467111Smckusick
21567111Smckusick dirp->dd_len = len;
21667111Smckusick dirp->dd_size = ddptr - dirp->dd_buf;
21767575Spendry } else {
21867111Smckusick dirp->dd_len = incr;
21967111Smckusick dirp->dd_buf = malloc(dirp->dd_len);
22067111Smckusick if (dirp->dd_buf == NULL) {
22167111Smckusick free(dirp);
22267111Smckusick close (fd);
22367111Smckusick return (NULL);
22467111Smckusick }
22567111Smckusick dirp->dd_seek = 0;
22667575Spendry flags &= ~DTF_REWIND;
22738435Smckusick }
22867111Smckusick
22967575Spendry dirp->dd_loc = 0;
2307659Smckusick dirp->dd_fd = fd;
23167575Spendry dirp->dd_flags = flags;
23267111Smckusick
23339957Smckusick /*
23439957Smckusick * Set up seek point for rewinddir.
23539957Smckusick */
23662720Smckusick dirp->dd_rewind = telldir(dirp);
23767111Smckusick
23867111Smckusick return (dirp);
2395750Smckusick }
240