1178253Sdelphij/*-
21573Srgrimes * Copyright (c) 1983, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * Redistribution and use in source and binary forms, with or without
61573Srgrimes * modification, are permitted provided that the following conditions
71573Srgrimes * are met:
81573Srgrimes * 1. Redistributions of source code must retain the above copyright
91573Srgrimes *    notice, this list of conditions and the following disclaimer.
101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111573Srgrimes *    notice, this list of conditions and the following disclaimer in the
121573Srgrimes *    documentation and/or other materials provided with the distribution.
131573Srgrimes * 4. Neither the name of the University nor the names of its contributors
141573Srgrimes *    may be used to endorse or promote products derived from this software
151573Srgrimes *    without specific prior written permission.
161573Srgrimes *
171573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271573Srgrimes * SUCH DAMAGE.
281573Srgrimes */
291573Srgrimes
301573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
3123668Speterstatic char sccsid[] = "@(#)opendir.c	8.8 (Berkeley) 5/1/95";
321573Srgrimes#endif /* LIBC_SCCS and not lint */
3390039Sobrien#include <sys/cdefs.h>
3490039Sobrien__FBSDID("$FreeBSD$");
351573Srgrimes
3671579Sdeischen#include "namespace.h"
371573Srgrimes#include <sys/param.h>
3823768Sbde#include <sys/mount.h>
397978Sbde#include <sys/stat.h>
401573Srgrimes
411573Srgrimes#include <dirent.h>
427978Sbde#include <errno.h>
431573Srgrimes#include <fcntl.h>
441573Srgrimes#include <stdlib.h>
45108631Stjr#include <string.h>
461573Srgrimes#include <unistd.h>
4771579Sdeischen#include "un-namespace.h"
481573Srgrimes
49235647Sgleb#include "gen-private.h"
5069841Sdeischen#include "telldir.h"
51178256Sdelphij
52247236Sjillesstatic DIR * __opendir_common(int, int);
53178256Sdelphij
541573Srgrimes/*
5523668Speter * Open a directory.
561573Srgrimes */
571573SrgrimesDIR *
58178253Sdelphijopendir(const char *name)
591573Srgrimes{
6073632Sobrien
6123668Speter	return (__opendir2(name, DTF_HIDEW|DTF_NODUP));
6223668Speter}
631573Srgrimes
64178256Sdelphij/*
65178256Sdelphij * Open a directory with existing file descriptor.
66178256Sdelphij */
6723668SpeterDIR *
68178256Sdelphijfdopendir(int fd)
69178256Sdelphij{
70232385Sru	struct stat statb;
71178256Sdelphij
72232385Sru	/* Check that fd is associated with a directory. */
73232385Sru	if (_fstat(fd, &statb) != 0)
74232385Sru		return (NULL);
75232385Sru	if (!S_ISDIR(statb.st_mode)) {
76232385Sru		errno = ENOTDIR;
77232385Sru		return (NULL);
78232385Sru	}
79232385Sru	if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
80232385Sru		return (NULL);
81247236Sjilles	return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP));
82178256Sdelphij}
83178256Sdelphij
84178256SdelphijDIR *
85178253Sdelphij__opendir2(const char *name, int flags)
8623668Speter{
8723668Speter	int fd;
88227852Sjilles	DIR *dir;
89227852Sjilles	int saved_errno;
9023668Speter
91232385Sru	if ((fd = _open(name,
92232385Sru	    O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1)
9323768Sbde		return (NULL);
94178256Sdelphij
95247236Sjilles	dir = __opendir_common(fd, flags);
96227852Sjilles	if (dir == NULL) {
97227852Sjilles		saved_errno = errno;
98227852Sjilles		_close(fd);
99227852Sjilles		errno = saved_errno;
100227852Sjilles	}
101227852Sjilles	return (dir);
102178256Sdelphij}
103178256Sdelphij
104201604Skibstatic int
105202679Sacheopendir_compar(const void *p1, const void *p2)
106201604Skib{
107201604Skib
108202572Sache	return (strcmp((*(const struct dirent **)p1)->d_name,
109201604Skib	    (*(const struct dirent **)p2)->d_name));
110201604Skib}
111201604Skib
112201604Skib/*
113178256Sdelphij * Common routine for opendir(3), __opendir2(3) and fdopendir(3).
114178256Sdelphij */
115178256Sdelphijstatic DIR *
116247236Sjilles__opendir_common(int fd, int flags)
117178256Sdelphij{
118178256Sdelphij	DIR *dirp;
119178256Sdelphij	int incr;
120178256Sdelphij	int saved_errno;
121178256Sdelphij	int unionstack;
122227852Sjilles	int fd2;
123178256Sdelphij
124247236Sjilles	fd2 = -1;
125247236Sjilles
126232385Sru	if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL)
127232385Sru		return (NULL);
12823668Speter
129133723Sstefanf	dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR));
13069841Sdeischen	LIST_INIT(&dirp->dd_td->td_locq);
13169841Sdeischen	dirp->dd_td->td_loccnt = 0;
13269841Sdeischen
1331573Srgrimes	/*
13423768Sbde	 * Use the system page size if that is a multiple of DIRBLKSIZ.
13523668Speter	 * Hopefully this can be a big win someday by allowing page
13671579Sdeischen	 * trades to user space to be done by _getdirentries().
1371573Srgrimes	 */
13823668Speter	incr = getpagesize();
13923668Speter	if ((incr % DIRBLKSIZ) != 0)
14023668Speter		incr = DIRBLKSIZ;
14123668Speter
14223668Speter	/*
14323668Speter	 * Determine whether this directory is the top of a union stack.
14423668Speter	 */
14523668Speter	if (flags & DTF_NODUP) {
14623668Speter		struct statfs sfb;
14723668Speter
14871579Sdeischen		if (_fstatfs(fd, &sfb) < 0)
14923768Sbde			goto fail;
150115047Stjr		unionstack = !strcmp(sfb.f_fstypename, "unionfs")
15175860Sjoerg		    || (sfb.f_flags & MNT_UNION);
15223668Speter	} else {
15323668Speter		unionstack = 0;
15423668Speter	}
15523668Speter
15623668Speter	if (unionstack) {
15723668Speter		int len = 0;
15823668Speter		int space = 0;
15923668Speter		char *buf = 0;
16023668Speter		char *ddptr = 0;
16123668Speter		char *ddeptr;
16223668Speter		int n;
16323668Speter		struct dirent **dpv;
16423668Speter
16523668Speter		/*
16623668Speter		 * The strategy here is to read all the directory
16723668Speter		 * entries into a buffer, sort the buffer, and
16823668Speter		 * remove duplicate entries by setting the inode
16923668Speter		 * number to zero.
170247236Sjilles		 *
171247236Sjilles		 * We reopen the directory because _getdirentries()
172247236Sjilles		 * on a MNT_UNION mount modifies the open directory,
173247236Sjilles		 * making it refer to the lower directory after the
174247236Sjilles		 * upper directory's entries are exhausted.
175247236Sjilles		 * This would otherwise break software that uses
176247236Sjilles		 * the directory descriptor for fchdir or *at
177247236Sjilles		 * functions, such as fts.c.
17823668Speter		 */
179247236Sjilles		if ((fd2 = _openat(fd, ".", O_RDONLY | O_CLOEXEC)) == -1) {
180247236Sjilles			saved_errno = errno;
181247236Sjilles			free(buf);
182247236Sjilles			free(dirp);
183247236Sjilles			errno = saved_errno;
184247236Sjilles			return (NULL);
185247236Sjilles		}
18623668Speter
18723668Speter		do {
18823668Speter			/*
18923668Speter			 * Always make at least DIRBLKSIZ bytes
19071579Sdeischen			 * available to _getdirentries
19123668Speter			 */
19223668Speter			if (space < DIRBLKSIZ) {
19323668Speter				space += incr;
19423668Speter				len += incr;
19539327Simp				buf = reallocf(buf, len);
19623768Sbde				if (buf == NULL)
19723768Sbde					goto fail;
19823668Speter				ddptr = buf + (len - space);
19923668Speter			}
20023668Speter
201247236Sjilles			n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek);
20223668Speter			if (n > 0) {
20323668Speter				ddptr += n;
20423668Speter				space -= n;
20523668Speter			}
20623668Speter		} while (n > 0);
20723668Speter
20823668Speter		ddeptr = ddptr;
20923668Speter		flags |= __DTF_READALL;
21023668Speter
211247236Sjilles		_close(fd2);
212247236Sjilles		fd2 = -1;
21323668Speter
21423668Speter		/*
21523668Speter		 * There is now a buffer full of (possibly) duplicate
21623668Speter		 * names.
21723668Speter		 */
21823668Speter		dirp->dd_buf = buf;
21923668Speter
22023668Speter		/*
22123668Speter		 * Go round this loop twice...
22223668Speter		 *
22323668Speter		 * Scan through the buffer, counting entries.
22423668Speter		 * On the second pass, save pointers to each one.
22523668Speter		 * Then sort the pointers and remove duplicate names.
22623668Speter		 */
22723668Speter		for (dpv = 0;;) {
22823668Speter			n = 0;
22923668Speter			ddptr = buf;
23023668Speter			while (ddptr < ddeptr) {
23123668Speter				struct dirent *dp;
23223668Speter
23323668Speter				dp = (struct dirent *) ddptr;
23434357Sjb				if ((long)dp & 03L)
23523668Speter					break;
23623668Speter				if ((dp->d_reclen <= 0) ||
23723668Speter				    (dp->d_reclen > (ddeptr + 1 - ddptr)))
23823668Speter					break;
23923668Speter				ddptr += dp->d_reclen;
24023668Speter				if (dp->d_fileno) {
24123668Speter					if (dpv)
24223668Speter						dpv[n] = dp;
24323668Speter					n++;
24423668Speter				}
24523668Speter			}
24623668Speter
24723668Speter			if (dpv) {
24823668Speter				struct dirent *xp;
24923668Speter
25023668Speter				/*
25123668Speter				 * This sort must be stable.
25223668Speter				 */
253201604Skib				mergesort(dpv, n, sizeof(*dpv),
254202679Sache				    opendir_compar);
25523668Speter
25623668Speter				dpv[n] = NULL;
25723668Speter				xp = NULL;
25823668Speter
25923668Speter				/*
26023668Speter				 * Scan through the buffer in sort order,
26123668Speter				 * zapping the inode number of any
26223668Speter				 * duplicate names.
26323668Speter				 */
26423668Speter				for (n = 0; dpv[n]; n++) {
26523668Speter					struct dirent *dp = dpv[n];
26623668Speter
26723668Speter					if ((xp == NULL) ||
26823668Speter					    strcmp(dp->d_name, xp->d_name)) {
26923668Speter						xp = dp;
27023668Speter					} else {
27123668Speter						dp->d_fileno = 0;
27223668Speter					}
27323668Speter					if (dp->d_type == DT_WHT &&
27423668Speter					    (flags & DTF_HIDEW))
27523668Speter						dp->d_fileno = 0;
27623668Speter				}
27723668Speter
27823668Speter				free(dpv);
27923668Speter				break;
28023668Speter			} else {
28123668Speter				dpv = malloc((n+1) * sizeof(struct dirent *));
28223668Speter				if (dpv == NULL)
28323668Speter					break;
28423668Speter			}
28523668Speter		}
28623668Speter
28723668Speter		dirp->dd_len = len;
28823668Speter		dirp->dd_size = ddptr - dirp->dd_buf;
28923668Speter	} else {
29023668Speter		dirp->dd_len = incr;
291123861Sdfr		dirp->dd_size = 0;
29223668Speter		dirp->dd_buf = malloc(dirp->dd_len);
29323768Sbde		if (dirp->dd_buf == NULL)
29423768Sbde			goto fail;
29523668Speter		dirp->dd_seek = 0;
29623668Speter	}
29723668Speter
29823668Speter	dirp->dd_loc = 0;
2991573Srgrimes	dirp->dd_fd = fd;
30023668Speter	dirp->dd_flags = flags;
30171579Sdeischen	dirp->dd_lock = NULL;
30223668Speter
3031573Srgrimes	/*
3041573Srgrimes	 * Set up seek point for rewinddir.
3051573Srgrimes	 */
3061573Srgrimes	dirp->dd_rewind = telldir(dirp);
3077978Sbde
30823668Speter	return (dirp);
30923768Sbde
31023768Sbdefail:
31123768Sbde	saved_errno = errno;
312247236Sjilles	if (fd2 != -1)
313247236Sjilles		_close(fd2);
31423768Sbde	free(dirp);
31523768Sbde	errno = saved_errno;
31623768Sbde	return (NULL);
3171573Srgrimes}
318