diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index c146d4d..43a177f 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -4,13 +4,13 @@ # machine-independent gen sources .PATH: ${.CURDIR}/${MACHINE_ARCH}/gen ${.CURDIR}/gen -SRCS+= __getosreldate.c __xuname.c \ +SRCS+= __fdopendir2.c __getosreldate.c __xuname.c \ _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \ alarm.c arc4random.c assert.c basename.c check_utility_compat.c \ clock.c closedir.c confstr.c \ crypt.c ctermid.c daemon.c devname.c dirname.c disklabel.c \ dlfcn.c dlfunc.c drand48.c erand48.c err.c errlst.c errno.c \ - exec.c feature_present.c fmtcheck.c fmtmsg.c fnmatch.c \ + exec.c fdopendir.c feature_present.c fmtcheck.c fmtmsg.c fnmatch.c \ fpclassify.c frexp.c fstab.c ftok.c fts.c fts-compat.c ftw.c \ getbootfile.c getbsize.c \ getcap.c getcwd.c getdomainname.c getgrent.c getgrouplist.c \ @@ -71,9 +71,9 @@ MAN+= alarm.3 arc4random.3 \ MLINKS+=arc4random.3 arc4random_addrandom.3 arc4random.3 arc4random_stir.3 MLINKS+=ctermid.3 ctermid_r.3 MLINKS+=devname.3 devname_r.3 -MLINKS+=directory.3 closedir.3 directory.3 dirfd.3 directory.3 opendir.3 \ - directory.3 readdir.3 directory.3 readdir_r.3 directory.3 rewinddir.3 \ - directory.3 seekdir.3 directory.3 telldir.3 +MLINKS+=directory.3 closedir.3 directory.3 dirfd.3 directory.3 fdopendir.3 \ + directory.3 opendir.3 directory.3 readdir.3 directory.3 readdir_r.3 \ + directory.3 rewinddir.3 directory.3 seekdir.3 directory.3 telldir.3 MLINKS+=dlopen.3 dlclose.3 dlopen.3 dlerror.3 dlopen.3 dlfunc.3 \ dlopen.3 dlsym.3 MLINKS+=err.3 err_set_exit.3 err.3 err_set_file.3 err.3 errc.3 err.3 errx.3 \ diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map index 2bfda74..89a1320 100644 --- a/lib/libc/gen/Symbol.map +++ b/lib/libc/gen/Symbol.map @@ -329,6 +329,7 @@ FBSD_1.0 { }; FBSD_1.1 { + fdopendir; fts_open; fts_close; fts_read; diff --git a/lib/libc/gen/__fdopendir2.c b/lib/libc/gen/__fdopendir2.c new file mode 100644 index 0000000..a8dbfb8 --- /dev/null +++ b/lib/libc/gen/__fdopendir2.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)opendir.c 8.8 (Berkeley) 5/1/95"; +#endif /* LIBC_SCCS and not lint */ +#include +__FBSDID("$FreeBSD: src/lib/libc/gen/opendir.c,v 1.23 2007/01/09 00:27:54 imp Exp $"); + +#include "namespace.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "un-namespace.h" + +#include "telldir.h" + +struct _dirdesc * +__fdopendir2(const char *name, int fd1, int flags) +{ + DIR *dirp; + int fd; + int incr; + int saved_errno; + int unionstack; + struct stat statb; + + /* + * Rewind may be done only when the name is known. + */ + if (fd == -1 && (flags & DTF_REWIND)) { + errno = EINVAL; + return (NULL); + } + dirp = NULL; + if (fd1 == -1) { + /* + * stat() before _open() because opening of special files may be + * harmful. _fstat() after open because the file may have changed. + */ + if (stat(name, &statb) != 0) + return (NULL); + if (!S_ISDIR(statb.st_mode)) { + errno = ENOTDIR; + return (NULL); + } + if ((fd = _open(name, O_RDONLY | O_NONBLOCK)) == -1) + return (NULL); + } else + fd = fd1; + if (_fstat(fd, &statb) != 0) + return (NULL); + if (!S_ISDIR(statb.st_mode)) { + errno = ENOTDIR; + return (NULL); + } + if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || + (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) + return (NULL); + + dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); + LIST_INIT(&dirp->dd_td->td_locq); + dirp->dd_td->td_loccnt = 0; + + /* + * Use the system page size if that is a multiple of DIRBLKSIZ. + * Hopefully this can be a big win someday by allowing page + * trades to user space to be done by _getdirentries(). + */ + incr = getpagesize(); + if ((incr % DIRBLKSIZ) != 0) + incr = DIRBLKSIZ; + + /* + * Determine whether this directory is the top of a union stack. + */ + if (flags & DTF_NODUP) { + struct statfs sfb; + + if (_fstatfs(fd, &sfb) < 0) + goto fail; + unionstack = !strcmp(sfb.f_fstypename, "unionfs") + || (sfb.f_flags & MNT_UNION); + } else { + unionstack = 0; + } + + if (unionstack) { + int len = 0; + int space = 0; + char *buf = 0; + char *ddptr = 0; + char *ddeptr; + int n; + struct dirent **dpv; + + /* + * The strategy here is to read all the directory + * entries into a buffer, sort the buffer, and + * remove duplicate entries by setting the inode + * number to zero. + */ + + do { + /* + * Always make at least DIRBLKSIZ bytes + * available to _getdirentries + */ + if (space < DIRBLKSIZ) { + space += incr; + len += incr; + buf = reallocf(buf, len); + if (buf == NULL) + goto fail; + ddptr = buf + (len - space); + } + + n = _getdirentries(fd, ddptr, space, &dirp->dd_seek); + if (n > 0) { + ddptr += n; + space -= n; + } + } while (n > 0); + + ddeptr = ddptr; + flags |= __DTF_READALL; + + /* + * Re-open the directory. + * This has the effect of rewinding back to the + * top of the union stack and is needed by + * programs which plan to fchdir to a descriptor + * which has also been read -- see fts.c. + */ + if (flags & DTF_REWIND) { + (void)_close(fd); + if ((fd = _open(name, O_RDONLY)) == -1) { + saved_errno = errno; + free(buf); + free(dirp); + errno = saved_errno; + return (NULL); + } + } + + /* + * There is now a buffer full of (possibly) duplicate + * names. + */ + dirp->dd_buf = buf; + + /* + * Go round this loop twice... + * + * Scan through the buffer, counting entries. + * On the second pass, save pointers to each one. + * Then sort the pointers and remove duplicate names. + */ + for (dpv = 0;;) { + n = 0; + ddptr = buf; + while (ddptr < ddeptr) { + struct dirent *dp; + + dp = (struct dirent *) ddptr; + if ((long)dp & 03L) + break; + if ((dp->d_reclen <= 0) || + (dp->d_reclen > (ddeptr + 1 - ddptr))) + break; + ddptr += dp->d_reclen; + if (dp->d_fileno) { + if (dpv) + dpv[n] = dp; + n++; + } + } + + if (dpv) { + struct dirent *xp; + + /* + * This sort must be stable. + */ + mergesort(dpv, n, sizeof(*dpv), alphasort); + + dpv[n] = NULL; + xp = NULL; + + /* + * Scan through the buffer in sort order, + * zapping the inode number of any + * duplicate names. + */ + for (n = 0; dpv[n]; n++) { + struct dirent *dp = dpv[n]; + + if ((xp == NULL) || + strcmp(dp->d_name, xp->d_name)) { + xp = dp; + } else { + dp->d_fileno = 0; + } + if (dp->d_type == DT_WHT && + (flags & DTF_HIDEW)) + dp->d_fileno = 0; + } + + free(dpv); + break; + } else { + dpv = malloc((n+1) * sizeof(struct dirent *)); + if (dpv == NULL) + break; + } + } + + dirp->dd_len = len; + dirp->dd_size = ddptr - dirp->dd_buf; + } else { + dirp->dd_len = incr; + dirp->dd_size = 0; + dirp->dd_buf = malloc(dirp->dd_len); + if (dirp->dd_buf == NULL) + goto fail; + dirp->dd_seek = 0; + flags &= ~DTF_REWIND; + } + + dirp->dd_loc = 0; + dirp->dd_fd = fd; + dirp->dd_flags = flags; + dirp->dd_lock = NULL; + + /* + * Set up seek point for rewinddir. + */ + dirp->dd_rewind = telldir(dirp); + + return (dirp); + +fail: + if (fd1 == -1) { + saved_errno = errno; + (void)_close(fd); + errno = saved_errno; + } + free(dirp); + return (NULL); +} diff --git a/lib/libc/gen/directory.3 b/lib/libc/gen/directory.3 index f80d8c9..bd45629 100644 --- a/lib/libc/gen/directory.3 +++ b/lib/libc/gen/directory.3 @@ -33,6 +33,7 @@ .Os .Sh NAME .Nm opendir , +.Nm fdopendir , .Nm readdir , .Nm readdir_r , .Nm telldir , @@ -48,6 +49,8 @@ .In dirent.h .Ft DIR * .Fn opendir "const char *filename" +.Ft DIR * +.Fn fdopendir "int fd" .Ft struct dirent * .Fn readdir "DIR *dirp" .Ft int @@ -84,6 +87,36 @@ cannot be accessed, or if it cannot enough memory to hold the whole thing. .Pp The +.Fn fdopendir +function is equivalent to the +.Fn opendir +function except that the directory is specified by a file descriptor +.Fa fd +rather than by a name. +The file offset associated with the file descriptor at the time of the call +determines which entries are returned. +.Pp +Upon successful return from +.Fn fdopendir , +the file descriptor is under the control of the system, +and if any attempt is made to close the file descriptor, +or to modify the state of the associated description other than by means +of +.Fn closedir , +.Fn readdir , +.Fn readdir_r , +or +.Fn rewinddir , +the behavior is undefined. +Upon calling +.Fn closedir +the file descriptor is closed. +The +.Dv FD_CLOEXEC +flag is set on the file descriptor by a successful call to +.Fn fdopendir . +.Pp +The .Fn readdir function returns a pointer to the next directory entry. @@ -202,3 +235,7 @@ and .Fn dirfd functions appeared in .Bx 4.2 . +The +.Fn fdopendir +function appeared in +.Fx 8.0 . diff --git a/lib/libc/gen/fdopendir.c b/lib/libc/gen/fdopendir.c new file mode 100644 index 0000000..7af7ee4 --- /dev/null +++ b/lib/libc/gen/fdopendir.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008 Konstantin Belousov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "namespace.h" +#include + +#include +#include +#include "un-namespace.h" + +#include "telldir.h" + +DIR * +fdopendir(int fd) +{ + + if (fd == -1) { + errno = EBADF; + return (NULL); + } + return (__fdopendir2(NULL, fd, DTF_HIDEW|DTF_NODUP)); +} diff --git a/lib/libc/gen/opendir.c b/lib/libc/gen/opendir.c index 4266b76..5de126f 100644 --- a/lib/libc/gen/opendir.c +++ b/lib/libc/gen/opendir.c @@ -35,15 +35,8 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/opendir.c,v 1.23 2007/01/09 00:27:54 imp Ex #include "namespace.h" #include -#include -#include #include -#include -#include -#include -#include -#include #include "un-namespace.h" #include "telldir.h" @@ -63,221 +56,6 @@ __opendir2(name, flags) const char *name; int flags; { - DIR *dirp; - int fd; - int incr; - int saved_errno; - int unionstack; - struct stat statb; - /* - * stat() before _open() because opening of special files may be - * harmful. _fstat() after open because the file may have changed. - */ - if (stat(name, &statb) != 0) - return (NULL); - if (!S_ISDIR(statb.st_mode)) { - errno = ENOTDIR; - return (NULL); - } - if ((fd = _open(name, O_RDONLY | O_NONBLOCK)) == -1) - return (NULL); - dirp = NULL; - if (_fstat(fd, &statb) != 0) - goto fail; - if (!S_ISDIR(statb.st_mode)) { - errno = ENOTDIR; - goto fail; - } - if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 || - (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) - goto fail; - - dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); - LIST_INIT(&dirp->dd_td->td_locq); - dirp->dd_td->td_loccnt = 0; - - /* - * Use the system page size if that is a multiple of DIRBLKSIZ. - * Hopefully this can be a big win someday by allowing page - * trades to user space to be done by _getdirentries(). - */ - incr = getpagesize(); - if ((incr % DIRBLKSIZ) != 0) - incr = DIRBLKSIZ; - - /* - * Determine whether this directory is the top of a union stack. - */ - if (flags & DTF_NODUP) { - struct statfs sfb; - - if (_fstatfs(fd, &sfb) < 0) - goto fail; - unionstack = !strcmp(sfb.f_fstypename, "unionfs") - || (sfb.f_flags & MNT_UNION); - } else { - unionstack = 0; - } - - if (unionstack) { - int len = 0; - int space = 0; - char *buf = 0; - char *ddptr = 0; - char *ddeptr; - int n; - struct dirent **dpv; - - /* - * The strategy here is to read all the directory - * entries into a buffer, sort the buffer, and - * remove duplicate entries by setting the inode - * number to zero. - */ - - do { - /* - * Always make at least DIRBLKSIZ bytes - * available to _getdirentries - */ - if (space < DIRBLKSIZ) { - space += incr; - len += incr; - buf = reallocf(buf, len); - if (buf == NULL) - goto fail; - ddptr = buf + (len - space); - } - - n = _getdirentries(fd, ddptr, space, &dirp->dd_seek); - if (n > 0) { - ddptr += n; - space -= n; - } - } while (n > 0); - - ddeptr = ddptr; - flags |= __DTF_READALL; - - /* - * Re-open the directory. - * This has the effect of rewinding back to the - * top of the union stack and is needed by - * programs which plan to fchdir to a descriptor - * which has also been read -- see fts.c. - */ - if (flags & DTF_REWIND) { - (void)_close(fd); - if ((fd = _open(name, O_RDONLY)) == -1) { - saved_errno = errno; - free(buf); - free(dirp); - errno = saved_errno; - return (NULL); - } - } - - /* - * There is now a buffer full of (possibly) duplicate - * names. - */ - dirp->dd_buf = buf; - - /* - * Go round this loop twice... - * - * Scan through the buffer, counting entries. - * On the second pass, save pointers to each one. - * Then sort the pointers and remove duplicate names. - */ - for (dpv = 0;;) { - n = 0; - ddptr = buf; - while (ddptr < ddeptr) { - struct dirent *dp; - - dp = (struct dirent *) ddptr; - if ((long)dp & 03L) - break; - if ((dp->d_reclen <= 0) || - (dp->d_reclen > (ddeptr + 1 - ddptr))) - break; - ddptr += dp->d_reclen; - if (dp->d_fileno) { - if (dpv) - dpv[n] = dp; - n++; - } - } - - if (dpv) { - struct dirent *xp; - - /* - * This sort must be stable. - */ - mergesort(dpv, n, sizeof(*dpv), alphasort); - - dpv[n] = NULL; - xp = NULL; - - /* - * Scan through the buffer in sort order, - * zapping the inode number of any - * duplicate names. - */ - for (n = 0; dpv[n]; n++) { - struct dirent *dp = dpv[n]; - - if ((xp == NULL) || - strcmp(dp->d_name, xp->d_name)) { - xp = dp; - } else { - dp->d_fileno = 0; - } - if (dp->d_type == DT_WHT && - (flags & DTF_HIDEW)) - dp->d_fileno = 0; - } - - free(dpv); - break; - } else { - dpv = malloc((n+1) * sizeof(struct dirent *)); - if (dpv == NULL) - break; - } - } - - dirp->dd_len = len; - dirp->dd_size = ddptr - dirp->dd_buf; - } else { - dirp->dd_len = incr; - dirp->dd_size = 0; - dirp->dd_buf = malloc(dirp->dd_len); - if (dirp->dd_buf == NULL) - goto fail; - dirp->dd_seek = 0; - flags &= ~DTF_REWIND; - } - - dirp->dd_loc = 0; - dirp->dd_fd = fd; - dirp->dd_flags = flags; - dirp->dd_lock = NULL; - - /* - * Set up seek point for rewinddir. - */ - dirp->dd_rewind = telldir(dirp); - - return (dirp); - -fail: - saved_errno = errno; - free(dirp); - (void)_close(fd); - errno = saved_errno; - return (NULL); + return (__fdopendir2(name, -1, flags)); } diff --git a/lib/libc/gen/telldir.h b/lib/libc/gen/telldir.h index 82cf9c1..e371d6a 100644 --- a/lib/libc/gen/telldir.h +++ b/lib/libc/gen/telldir.h @@ -62,5 +62,6 @@ struct _telldir { struct dirent *_readdir_unlocked(DIR *); void _reclaim_telldir(DIR *); void _seekdir(DIR *, long); +struct _dirdesc *__fdopendir2(const char *, int, int); #endif