xref: /freebsd/lib/libc/gen/opendir.c (revision 8a16b7a18f5d0b031f09832fd7752fba717e2a97)
1f6386c25SXin LI /*-
2*8a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
3*8a16b7a1SPedro F. Giffuni  *
458f0484fSRodney W. Grimes  * Copyright (c) 1983, 1993
558f0484fSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
658f0484fSRodney W. Grimes  *
758f0484fSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
858f0484fSRodney W. Grimes  * modification, are permitted provided that the following conditions
958f0484fSRodney W. Grimes  * are met:
1058f0484fSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
1158f0484fSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
1258f0484fSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
1358f0484fSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
1458f0484fSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
1658f0484fSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
1758f0484fSRodney W. Grimes  *    without specific prior written permission.
1858f0484fSRodney W. Grimes  *
1958f0484fSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2058f0484fSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2158f0484fSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2258f0484fSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2358f0484fSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2458f0484fSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2558f0484fSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2658f0484fSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2758f0484fSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2858f0484fSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2958f0484fSRodney W. Grimes  * SUCH DAMAGE.
3058f0484fSRodney W. Grimes  */
3158f0484fSRodney W. Grimes 
3258f0484fSRodney W. Grimes #if defined(LIBC_SCCS) && !defined(lint)
33adf6ad9eSPeter Wemm static char sccsid[] = "@(#)opendir.c	8.8 (Berkeley) 5/1/95";
3458f0484fSRodney W. Grimes #endif /* LIBC_SCCS and not lint */
35ea8d448aSDavid E. O'Brien #include <sys/cdefs.h>
36ea8d448aSDavid E. O'Brien __FBSDID("$FreeBSD$");
3758f0484fSRodney W. Grimes 
38d201fe46SDaniel Eischen #include "namespace.h"
3958f0484fSRodney W. Grimes #include <sys/param.h>
40adf6ad9eSPeter Wemm #include <sys/mount.h>
4103dcee8dSBruce Evans #include <sys/stat.h>
4258f0484fSRodney W. Grimes 
4358f0484fSRodney W. Grimes #include <dirent.h>
4455e2b2c6SBruce Evans #include <errno.h>
4558f0484fSRodney W. Grimes #include <fcntl.h>
4658f0484fSRodney W. Grimes #include <stdlib.h>
4781b3ad59STim J. Robbins #include <string.h>
4858f0484fSRodney W. Grimes #include <unistd.h>
49d201fe46SDaniel Eischen #include "un-namespace.h"
5058f0484fSRodney W. Grimes 
510bb2aabfSGleb Kurtsou #include "gen-private.h"
5210d1cba0SDaniel Eischen #include "telldir.h"
536fda52baSXin LI 
549f72c032SJohn Baldwin static DIR * __opendir_common(int, int, bool);
556fda52baSXin LI 
5658f0484fSRodney W. Grimes /*
57adf6ad9eSPeter Wemm  * Open a directory.
5858f0484fSRodney W. Grimes  */
5958f0484fSRodney W. Grimes DIR *
60f6386c25SXin LI opendir(const char *name)
6158f0484fSRodney W. Grimes {
628bb47e40SDavid E. O'Brien 
63adf6ad9eSPeter Wemm 	return (__opendir2(name, DTF_HIDEW|DTF_NODUP));
64adf6ad9eSPeter Wemm }
65adf6ad9eSPeter Wemm 
666fda52baSXin LI /*
676fda52baSXin LI  * Open a directory with existing file descriptor.
686fda52baSXin LI  */
696fda52baSXin LI DIR *
706fda52baSXin LI fdopendir(int fd)
716fda52baSXin LI {
726fda52baSXin LI 
7301e49397SRuslan Ermilov 	if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
7401e49397SRuslan Ermilov 		return (NULL);
759f72c032SJohn Baldwin 	return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP, true));
766fda52baSXin LI }
776fda52baSXin LI 
78adf6ad9eSPeter Wemm DIR *
79f6386c25SXin LI __opendir2(const char *name, int flags)
80adf6ad9eSPeter Wemm {
81adf6ad9eSPeter Wemm 	int fd;
8238574aa8SJilles Tjoelker 	DIR *dir;
8338574aa8SJilles Tjoelker 	int saved_errno;
8458f0484fSRodney W. Grimes 
859f72c032SJohn Baldwin 	if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0)
869f72c032SJohn Baldwin 		return (NULL);
8701e49397SRuslan Ermilov 	if ((fd = _open(name,
8801e49397SRuslan Ermilov 	    O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1)
89adf6ad9eSPeter Wemm 		return (NULL);
906fda52baSXin LI 
919f72c032SJohn Baldwin 	dir = __opendir_common(fd, flags, false);
9238574aa8SJilles Tjoelker 	if (dir == NULL) {
9338574aa8SJilles Tjoelker 		saved_errno = errno;
9438574aa8SJilles Tjoelker 		_close(fd);
9538574aa8SJilles Tjoelker 		errno = saved_errno;
9638574aa8SJilles Tjoelker 	}
9738574aa8SJilles Tjoelker 	return (dir);
986fda52baSXin LI }
996fda52baSXin LI 
100f5636f88SKonstantin Belousov static int
101e5c9c853SAndrey A. Chernov opendir_compar(const void *p1, const void *p2)
102f5636f88SKonstantin Belousov {
103f5636f88SKonstantin Belousov 
104b9a74f2aSAndrey A. Chernov 	return (strcmp((*(const struct dirent **)p1)->d_name,
105f5636f88SKonstantin Belousov 	    (*(const struct dirent **)p2)->d_name));
106f5636f88SKonstantin Belousov }
107f5636f88SKonstantin Belousov 
108f5636f88SKonstantin Belousov /*
1099f72c032SJohn Baldwin  * For a directory at the top of a unionfs stack, the entire directory's
1109f72c032SJohn Baldwin  * contents are read and cached locally until the next call to rewinddir().
1119f72c032SJohn Baldwin  * For the fdopendir() case, the initial seek position must be preserved.
1129f72c032SJohn Baldwin  * For rewinddir(), the full directory should always be re-read from the
1139f72c032SJohn Baldwin  * beginning.
1149f72c032SJohn Baldwin  *
1159f72c032SJohn Baldwin  * If an error occurs, the existing buffer and state of 'dirp' is left
1169f72c032SJohn Baldwin  * unchanged.
1176fda52baSXin LI  */
1189f72c032SJohn Baldwin bool
1199f72c032SJohn Baldwin _filldir(DIR *dirp, bool use_current_pos)
1206fda52baSXin LI {
1219f72c032SJohn Baldwin 	struct dirent **dpv;
1229f72c032SJohn Baldwin 	char *buf, *ddptr, *ddeptr;
1239f72c032SJohn Baldwin 	off_t pos;
1249f72c032SJohn Baldwin 	int fd2, incr, len, n, saved_errno, space;
1256fda52baSXin LI 
1269f72c032SJohn Baldwin 	len = 0;
1279f72c032SJohn Baldwin 	space = 0;
1289f72c032SJohn Baldwin 	buf = NULL;
1299f72c032SJohn Baldwin 	ddptr = NULL;
13010d1cba0SDaniel Eischen 
13158f0484fSRodney W. Grimes 	/*
13203dcee8dSBruce Evans 	 * Use the system page size if that is a multiple of DIRBLKSIZ.
133adf6ad9eSPeter Wemm 	 * Hopefully this can be a big win someday by allowing page
134d201fe46SDaniel Eischen 	 * trades to user space to be done by _getdirentries().
13558f0484fSRodney W. Grimes 	 */
136adf6ad9eSPeter Wemm 	incr = getpagesize();
137adf6ad9eSPeter Wemm 	if ((incr % DIRBLKSIZ) != 0)
138adf6ad9eSPeter Wemm 		incr = DIRBLKSIZ;
139adf6ad9eSPeter Wemm 
140adf6ad9eSPeter Wemm 	/*
141adf6ad9eSPeter Wemm 	 * The strategy here is to read all the directory
142adf6ad9eSPeter Wemm 	 * entries into a buffer, sort the buffer, and
143adf6ad9eSPeter Wemm 	 * remove duplicate entries by setting the inode
144adf6ad9eSPeter Wemm 	 * number to zero.
145c6344d08SJilles Tjoelker 	 *
146c6344d08SJilles Tjoelker 	 * We reopen the directory because _getdirentries()
147c6344d08SJilles Tjoelker 	 * on a MNT_UNION mount modifies the open directory,
148c6344d08SJilles Tjoelker 	 * making it refer to the lower directory after the
149c6344d08SJilles Tjoelker 	 * upper directory's entries are exhausted.
150c6344d08SJilles Tjoelker 	 * This would otherwise break software that uses
151c6344d08SJilles Tjoelker 	 * the directory descriptor for fchdir or *at
152c6344d08SJilles Tjoelker 	 * functions, such as fts.c.
153adf6ad9eSPeter Wemm 	 */
1549f72c032SJohn Baldwin 	if ((fd2 = _openat(dirp->dd_fd, ".", O_RDONLY | O_CLOEXEC)) == -1)
1559f72c032SJohn Baldwin 		return (false);
1569f72c032SJohn Baldwin 
1579f72c032SJohn Baldwin 	if (use_current_pos) {
1589f72c032SJohn Baldwin 		pos = lseek(dirp->dd_fd, 0, SEEK_CUR);
1599f72c032SJohn Baldwin 		if (pos == -1 || lseek(fd2, pos, SEEK_SET) == -1) {
160c6344d08SJilles Tjoelker 			saved_errno = errno;
1619f72c032SJohn Baldwin 			_close(fd2);
162c6344d08SJilles Tjoelker 			errno = saved_errno;
1639f72c032SJohn Baldwin 			return (false);
1649f72c032SJohn Baldwin 		}
165c6344d08SJilles Tjoelker 	}
166adf6ad9eSPeter Wemm 
167adf6ad9eSPeter Wemm 	do {
168adf6ad9eSPeter Wemm 		/*
169adf6ad9eSPeter Wemm 		 * Always make at least DIRBLKSIZ bytes
170d201fe46SDaniel Eischen 		 * available to _getdirentries
171adf6ad9eSPeter Wemm 		 */
172adf6ad9eSPeter Wemm 		if (space < DIRBLKSIZ) {
173adf6ad9eSPeter Wemm 			space += incr;
174adf6ad9eSPeter Wemm 			len += incr;
175e8420087SWarner Losh 			buf = reallocf(buf, len);
1769f72c032SJohn Baldwin 			if (buf == NULL) {
1779f72c032SJohn Baldwin 				saved_errno = errno;
1789f72c032SJohn Baldwin 				_close(fd2);
1799f72c032SJohn Baldwin 				errno = saved_errno;
1809f72c032SJohn Baldwin 				return (false);
1819f72c032SJohn Baldwin 			}
182adf6ad9eSPeter Wemm 			ddptr = buf + (len - space);
183adf6ad9eSPeter Wemm 		}
184adf6ad9eSPeter Wemm 
185c6344d08SJilles Tjoelker 		n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek);
186adf6ad9eSPeter Wemm 		if (n > 0) {
187adf6ad9eSPeter Wemm 			ddptr += n;
188adf6ad9eSPeter Wemm 			space -= n;
189adf6ad9eSPeter Wemm 		}
1909f72c032SJohn Baldwin 		if (n < 0) {
1919f72c032SJohn Baldwin 			saved_errno = errno;
1929f72c032SJohn Baldwin 			_close(fd2);
1939f72c032SJohn Baldwin 			errno = saved_errno;
1949f72c032SJohn Baldwin 			return (false);
1959f72c032SJohn Baldwin 		}
196adf6ad9eSPeter Wemm 	} while (n > 0);
1979f72c032SJohn Baldwin 	_close(fd2);
198adf6ad9eSPeter Wemm 
199adf6ad9eSPeter Wemm 	ddeptr = ddptr;
200adf6ad9eSPeter Wemm 
201adf6ad9eSPeter Wemm 	/*
202adf6ad9eSPeter Wemm 	 * There is now a buffer full of (possibly) duplicate
203adf6ad9eSPeter Wemm 	 * names.
204adf6ad9eSPeter Wemm 	 */
205adf6ad9eSPeter Wemm 	dirp->dd_buf = buf;
206adf6ad9eSPeter Wemm 
207adf6ad9eSPeter Wemm 	/*
208adf6ad9eSPeter Wemm 	 * Go round this loop twice...
209adf6ad9eSPeter Wemm 	 *
210adf6ad9eSPeter Wemm 	 * Scan through the buffer, counting entries.
211adf6ad9eSPeter Wemm 	 * On the second pass, save pointers to each one.
212adf6ad9eSPeter Wemm 	 * Then sort the pointers and remove duplicate names.
213adf6ad9eSPeter Wemm 	 */
214513004a2SPedro F. Giffuni 	for (dpv = NULL;;) {
215adf6ad9eSPeter Wemm 		n = 0;
216adf6ad9eSPeter Wemm 		ddptr = buf;
217adf6ad9eSPeter Wemm 		while (ddptr < ddeptr) {
218adf6ad9eSPeter Wemm 			struct dirent *dp;
219adf6ad9eSPeter Wemm 
220adf6ad9eSPeter Wemm 			dp = (struct dirent *) ddptr;
22184d65005SJohn Birrell 			if ((long)dp & 03L)
222adf6ad9eSPeter Wemm 				break;
223adf6ad9eSPeter Wemm 			if ((dp->d_reclen <= 0) ||
224adf6ad9eSPeter Wemm 			    (dp->d_reclen > (ddeptr + 1 - ddptr)))
225adf6ad9eSPeter Wemm 				break;
226adf6ad9eSPeter Wemm 			ddptr += dp->d_reclen;
227adf6ad9eSPeter Wemm 			if (dp->d_fileno) {
228adf6ad9eSPeter Wemm 				if (dpv)
229adf6ad9eSPeter Wemm 					dpv[n] = dp;
230adf6ad9eSPeter Wemm 				n++;
231adf6ad9eSPeter Wemm 			}
232adf6ad9eSPeter Wemm 		}
233adf6ad9eSPeter Wemm 
234adf6ad9eSPeter Wemm 		if (dpv) {
235adf6ad9eSPeter Wemm 			struct dirent *xp;
236adf6ad9eSPeter Wemm 
237adf6ad9eSPeter Wemm 			/*
238adf6ad9eSPeter Wemm 			 * This sort must be stable.
239adf6ad9eSPeter Wemm 			 */
2409f72c032SJohn Baldwin 			mergesort(dpv, n, sizeof(*dpv), opendir_compar);
241adf6ad9eSPeter Wemm 
242adf6ad9eSPeter Wemm 			dpv[n] = NULL;
243adf6ad9eSPeter Wemm 			xp = NULL;
244adf6ad9eSPeter Wemm 
245adf6ad9eSPeter Wemm 			/*
246adf6ad9eSPeter Wemm 			 * Scan through the buffer in sort order,
247adf6ad9eSPeter Wemm 			 * zapping the inode number of any
248adf6ad9eSPeter Wemm 			 * duplicate names.
249adf6ad9eSPeter Wemm 			 */
250adf6ad9eSPeter Wemm 			for (n = 0; dpv[n]; n++) {
251adf6ad9eSPeter Wemm 				struct dirent *dp = dpv[n];
252adf6ad9eSPeter Wemm 
253adf6ad9eSPeter Wemm 				if ((xp == NULL) ||
254adf6ad9eSPeter Wemm 				    strcmp(dp->d_name, xp->d_name)) {
255adf6ad9eSPeter Wemm 					xp = dp;
256adf6ad9eSPeter Wemm 				} else {
257adf6ad9eSPeter Wemm 					dp->d_fileno = 0;
258adf6ad9eSPeter Wemm 				}
259adf6ad9eSPeter Wemm 				if (dp->d_type == DT_WHT &&
2609f72c032SJohn Baldwin 				    (dirp->dd_flags & DTF_HIDEW))
261adf6ad9eSPeter Wemm 					dp->d_fileno = 0;
262adf6ad9eSPeter Wemm 			}
263adf6ad9eSPeter Wemm 
264adf6ad9eSPeter Wemm 			free(dpv);
265adf6ad9eSPeter Wemm 			break;
266adf6ad9eSPeter Wemm 		} else {
267adf6ad9eSPeter Wemm 			dpv = malloc((n+1) * sizeof(struct dirent *));
268adf6ad9eSPeter Wemm 			if (dpv == NULL)
269adf6ad9eSPeter Wemm 				break;
270adf6ad9eSPeter Wemm 		}
271adf6ad9eSPeter Wemm 	}
272adf6ad9eSPeter Wemm 
273adf6ad9eSPeter Wemm 	dirp->dd_len = len;
274adf6ad9eSPeter Wemm 	dirp->dd_size = ddptr - dirp->dd_buf;
2759f72c032SJohn Baldwin 	return (true);
2769f72c032SJohn Baldwin }
2779f72c032SJohn Baldwin 
2789f72c032SJohn Baldwin 
2799f72c032SJohn Baldwin /*
2809f72c032SJohn Baldwin  * Common routine for opendir(3), __opendir2(3) and fdopendir(3).
2819f72c032SJohn Baldwin  */
2829f72c032SJohn Baldwin static DIR *
2839f72c032SJohn Baldwin __opendir_common(int fd, int flags, bool use_current_pos)
2849f72c032SJohn Baldwin {
2859f72c032SJohn Baldwin 	DIR *dirp;
2869f72c032SJohn Baldwin 	int incr;
2879f72c032SJohn Baldwin 	int saved_errno;
2889f72c032SJohn Baldwin 	int unionstack;
2899f72c032SJohn Baldwin 
2909f72c032SJohn Baldwin 	if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL)
2919f72c032SJohn Baldwin 		return (NULL);
2929f72c032SJohn Baldwin 
2939f72c032SJohn Baldwin 	dirp->dd_buf = NULL;
2949f72c032SJohn Baldwin 	dirp->dd_fd = fd;
2959f72c032SJohn Baldwin 	dirp->dd_flags = flags;
2969f72c032SJohn Baldwin 	dirp->dd_loc = 0;
2979f72c032SJohn Baldwin 	dirp->dd_lock = NULL;
2989f72c032SJohn Baldwin 	dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR));
2999f72c032SJohn Baldwin 	LIST_INIT(&dirp->dd_td->td_locq);
3009f72c032SJohn Baldwin 	dirp->dd_td->td_loccnt = 0;
30169921123SKonstantin Belousov 	dirp->dd_compat_de = NULL;
3029f72c032SJohn Baldwin 
3039f72c032SJohn Baldwin 	/*
3049f72c032SJohn Baldwin 	 * Use the system page size if that is a multiple of DIRBLKSIZ.
3059f72c032SJohn Baldwin 	 * Hopefully this can be a big win someday by allowing page
3069f72c032SJohn Baldwin 	 * trades to user space to be done by _getdirentries().
3079f72c032SJohn Baldwin 	 */
3089f72c032SJohn Baldwin 	incr = getpagesize();
3099f72c032SJohn Baldwin 	if ((incr % DIRBLKSIZ) != 0)
3109f72c032SJohn Baldwin 		incr = DIRBLKSIZ;
3119f72c032SJohn Baldwin 
3129f72c032SJohn Baldwin 	/*
3139f72c032SJohn Baldwin 	 * Determine whether this directory is the top of a union stack.
3149f72c032SJohn Baldwin 	 */
3159f72c032SJohn Baldwin 	if (flags & DTF_NODUP) {
3169f72c032SJohn Baldwin 		struct statfs sfb;
3179f72c032SJohn Baldwin 
3189f72c032SJohn Baldwin 		if (_fstatfs(fd, &sfb) < 0)
3199f72c032SJohn Baldwin 			goto fail;
3209f72c032SJohn Baldwin 		unionstack = !strcmp(sfb.f_fstypename, "unionfs")
3219f72c032SJohn Baldwin 		    || (sfb.f_flags & MNT_UNION);
3229f72c032SJohn Baldwin 	} else {
3239f72c032SJohn Baldwin 		unionstack = 0;
3249f72c032SJohn Baldwin 	}
3259f72c032SJohn Baldwin 
3269f72c032SJohn Baldwin 	if (unionstack) {
3279f72c032SJohn Baldwin 		if (!_filldir(dirp, use_current_pos))
3289f72c032SJohn Baldwin 			goto fail;
3299f72c032SJohn Baldwin 		dirp->dd_flags |= __DTF_READALL;
330adf6ad9eSPeter Wemm 	} else {
331adf6ad9eSPeter Wemm 		dirp->dd_len = incr;
332d71458eeSPoul-Henning Kamp 		dirp->dd_buf = malloc(dirp->dd_len);
33303dcee8dSBruce Evans 		if (dirp->dd_buf == NULL)
33403dcee8dSBruce Evans 			goto fail;
3359f72c032SJohn Baldwin 		if (use_current_pos) {
3369f72c032SJohn Baldwin 			/*
3379f72c032SJohn Baldwin 			 * Read the first batch of directory entries
3389f72c032SJohn Baldwin 			 * to prime dd_seek.  This also checks if the
3399f72c032SJohn Baldwin 			 * fd passed to fdopendir() is a directory.
3409f72c032SJohn Baldwin 			 */
3419f72c032SJohn Baldwin 			dirp->dd_size = _getdirentries(dirp->dd_fd,
3429f72c032SJohn Baldwin 			    dirp->dd_buf, dirp->dd_len, &dirp->dd_seek);
3439f72c032SJohn Baldwin 			if (dirp->dd_size < 0) {
3449f72c032SJohn Baldwin 				if (errno == EINVAL)
3459f72c032SJohn Baldwin 					errno = ENOTDIR;
3469f72c032SJohn Baldwin 				goto fail;
3479f72c032SJohn Baldwin 			}
3489f72c032SJohn Baldwin 			dirp->dd_flags |= __DTF_SKIPREAD;
3499f72c032SJohn Baldwin 		} else {
3509f72c032SJohn Baldwin 			dirp->dd_size = 0;
35158f0484fSRodney W. Grimes 			dirp->dd_seek = 0;
352adf6ad9eSPeter Wemm 		}
3539f72c032SJohn Baldwin 	}
35455e2b2c6SBruce Evans 
355adf6ad9eSPeter Wemm 	return (dirp);
35603dcee8dSBruce Evans 
35703dcee8dSBruce Evans fail:
35803dcee8dSBruce Evans 	saved_errno = errno;
3599f72c032SJohn Baldwin 	free(dirp->dd_buf);
36003dcee8dSBruce Evans 	free(dirp);
36103dcee8dSBruce Evans 	errno = saved_errno;
36203dcee8dSBruce Evans 	return (NULL);
36358f0484fSRodney W. Grimes }
364