From 33037645335d210b2411baeb1c9d4482d7d3d455 Mon Sep 17 00:00:00 2001 From: christos Date: Fri, 19 Aug 2005 02:04:54 +0000 Subject: [PATCH] 64 bit inode changes --- include/dirent.h | 40 +- include/fts.h | 36 +- lib/libc/gen/Makefile.inc | 20 +- lib/libc/gen/__fts13.c | 1262 ++-------------------------------- lib/libc/gen/__fts30.c | 1190 ++++++++++++++++++++++++++++++++ lib/libc/gen/__opendir30.c | 326 +++++++++ lib/libc/gen/__readdir30.c | 150 ++++ lib/libc/gen/__scandir30.c | 141 ++++ lib/libc/gen/alphasort.c | 63 ++ lib/libc/gen/fts.c | 36 +- lib/libc/gen/opendir.c | 321 +-------- lib/libc/gen/readdir.c | 147 +--- lib/libc/gen/scandir.c | 165 +---- lib/libc/shlib_version | 4 +- lib/libc/sys/Makefile.inc | 17 +- lib/libc/sys/__stat13.c | 115 ++++ lib/libc/sys/getdents.2 | 6 +- lib/libc/sys/getdents.c | 87 +++ lib/libc/sys/getdirentries.c | 5 +- lib/libc/sys/stat.c | 12 +- 20 files changed, 2242 insertions(+), 1901 deletions(-) create mode 100644 lib/libc/gen/__fts30.c create mode 100644 lib/libc/gen/__opendir30.c create mode 100644 lib/libc/gen/__readdir30.c create mode 100644 lib/libc/gen/__scandir30.c create mode 100644 lib/libc/gen/alphasort.c create mode 100644 lib/libc/sys/__stat13.c create mode 100644 lib/libc/sys/getdents.c diff --git a/include/dirent.h b/include/dirent.h index e2828a639ba5..8a2597957f3c 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -1,4 +1,4 @@ -/* $NetBSD: dirent.h,v 1.22 2005/02/03 04:39:32 perry Exp $ */ +/* $NetBSD: dirent.h,v 1.23 2005/08/19 02:05:59 christos Exp $ */ /*- * Copyright (c) 1989, 1993 @@ -85,22 +85,46 @@ struct _dirdesc { __BEGIN_DECLS int closedir(DIR *); -DIR *opendir(const char *); -struct dirent *readdir(DIR *); -int readdir_r(DIR *, struct dirent * __restrict, struct dirent ** __restrict); void rewinddir(DIR *); +#ifdef __LIBC12_SOURCE__ +DIR *opendir(const char *); +struct dirent12 *readdir(DIR *); +int readdir_r(DIR *, struct dirent12 * __restrict, + struct dirent12 ** __restrict); +struct dirent *__readdir30(DIR *); +int __readdir_r30(DIR *, struct dirent * __restrict, + struct dirent ** __restrict); +#else +DIR *opendir(const char *) __RENAME(__opendir30); +DIR *__opendir30(const char *) __RENAME(__opendir30); +struct dirent *readdir(DIR *) __RENAME(__readdir30); +int readdir_r(DIR *, struct dirent * __restrict, + struct dirent ** __restrict) __RENAME(__readdir_r30); +#endif #if defined(_XOPEN_SOURCE) || defined(_NETBSD_SOURCE) void seekdir(DIR *, long); long telldir(const DIR *); #endif /* defined(_NETBSD_SOURCE) || defined(_XOPEN_SOURCE) */ #if defined(_NETBSD_SOURCE) -DIR *__opendir2(const char *, int); void __seekdir(DIR *, long); -int scandir(const char *, struct dirent ***, +#ifdef __LIBC12_SOURCE__ +DIR *__opendir2(const char *, int); +DIR *__opendir230(const char *, int); +int scandir(const char *, struct dirent12 ***, + int (*)(const struct dirent12 *), int (*)(const void *, const void *)); +int __scandir30(const char *, struct dirent ***, int (*)(const struct dirent *), int (*)(const void *, const void *)); -int alphasort(const void *, const void *); -int getdirentries(int, char *, int, long *); int getdents(int, char *, size_t); +int getdirentries(int, char *, int, long *); +int __getdents30(int, char *, size_t); +#else +DIR *__opendir2(const char *, int) __RENAME(__opendir230); +int scandir(const char *, struct dirent ***, + int (*)(const struct dirent *), int (*)(const void *, const void *)) + __RENAME(__scandir30); +int getdents(int, char *, size_t) __RENAME(__getdents30); +#endif +int alphasort(const void *, const void *); #endif /* defined(_NETBSD_SOURCE) */ __END_DECLS diff --git a/include/fts.h b/include/fts.h index b5ba9e3d7b76..9df2f768076b 100644 --- a/include/fts.h +++ b/include/fts.h @@ -1,4 +1,4 @@ -/* $NetBSD: fts.h,v 1.12 2005/02/03 04:39:32 perry Exp $ */ +/* $NetBSD: fts.h,v 1.13 2005/08/19 02:05:59 christos Exp $ */ /* * Copyright (c) 1989, 1993 @@ -34,6 +34,16 @@ #ifndef _FTS_H_ #define _FTS_H_ +#ifndef __fts_stat_t +#define __fts_stat_t struct stat +#endif +#ifndef __fts_nlink_t +#define __fts_nlink_t nlink_t +#endif +#ifndef __fts_ino_t +#define __fts_ino_t ino_t +#endif + typedef struct { struct _ftsent *fts_cur; /* current node */ struct _ftsent *fts_child; /* linked list of children */ @@ -74,13 +84,9 @@ typedef struct _ftsent { u_short fts_pathlen; /* strlen(fts_path) */ u_short fts_namelen; /* strlen(fts_name) */ - ino_t fts_ino; /* inode */ + __fts_ino_t fts_ino; /* inode */ dev_t fts_dev; /* device */ -#ifdef __LIBC12_SOURCE__ - u_int16_t fts_nlink; /* link count */ -#else - nlink_t fts_nlink; /* link count */ -#endif + __fts_nlink_t fts_nlink; /* link count */ #define FTS_ROOTPARENTLEVEL -1 #define FTS_ROOTLEVEL 0 @@ -113,11 +119,7 @@ typedef struct _ftsent { #define FTS_SKIP 4 /* discard node */ u_short fts_instr; /* fts_set() instructions */ -#ifdef __LIBC12_SOURCE__ - struct stat12 *fts_statp; /* stat(2) information */ -#else - struct stat *fts_statp; /* stat(2) information */ -#endif + __fts_stat_t *fts_statp; /* stat(2) information */ char fts_name[1]; /* file name */ } FTSENT; @@ -132,13 +134,13 @@ FTS *fts_open(char * const *, int, FTSENT *fts_read(FTS *); int fts_set(FTS *, FTSENT *, int); #else -FTSENT *fts_children(FTS *, int) __RENAME(__fts_children13); -int fts_close(FTS *) __RENAME(__fts_close13); +FTSENT *fts_children(FTS *, int) __RENAME(__fts_children30); +int fts_close(FTS *) __RENAME(__fts_close30); FTS *fts_open(char * const *, int, int (*)(const FTSENT **, const FTSENT **)) - __RENAME(__fts_open13); -FTSENT *fts_read(FTS *) __RENAME(__fts_read13); -int fts_set(FTS *, FTSENT *, int) __RENAME(__fts_set13); + __RENAME(__fts_open30); +FTSENT *fts_read(FTS *) __RENAME(__fts_read30); +int fts_set(FTS *, FTSENT *, int) __RENAME(__fts_set30); #endif __END_DECLS diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index 629f3b88f573..4dce57b1cfc5 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -1,15 +1,15 @@ -# $NetBSD: Makefile.inc,v 1.143 2005/04/12 16:27:42 drochner Exp $ +# $NetBSD: Makefile.inc,v 1.144 2005/08/19 02:04:54 christos Exp $ # from: @(#)Makefile.inc 8.6 (Berkeley) 5/4/95 # gen sources .PATH: ${ARCHDIR}/gen ${.CURDIR}/gen -SRCS+= _errno.c alarm.c arc4random.c assert.c basename.c clock.c closedir.c \ - closefrom.c confstr.c ctermid.c ctype_.c daemon.c devname.c dirname.c \ - disklabel.c err.c errx.c __errlist14.c errlist.c errno.c execl.c \ - execle.c execlp.c execv.c execvp.c extattr.c \ - fmtcheck.c fmtmsg.c fnmatch.c \ - fstab.c ftok.c __fts13.c fts.c getbsize.c getcap.c getcwd.c \ +SRCS+= _errno.c alarm.c alphasort.c arc4random.c assert.c basename.c clock.c \ + closedir.c closefrom.c confstr.c ctermid.c ctype_.c daemon.c \ + devname.c dirname.c disklabel.c err.c errx.c __errlist14.c errlist.c \ + errno.c execl.c execle.c execlp.c execv.c execvp.c extattr.c \ + fmtcheck.c fmtmsg.c fnmatch.c fstab.c ftok.c __fts13.c __fts30.c \ + fts.c getbsize.c getcap.c getcwd.c \ getdevmajor.c getdomainname.c getgrent.c \ getgrouplist.c getgroupmembership.c gethostname.c \ getloadavg.c getlogin.c getmntinfo.c __getmntinfo13.c \ @@ -18,9 +18,9 @@ SRCS+= _errno.c alarm.c arc4random.c assert.c basename.c clock.c closedir.c \ getusershell.c __glob13.c glob.c humanize_number.c initgroups.c \ isascii.c isatty.c isctype.c lockf.c nice.c nlist.c nlist_aout.c \ nlist_coff.c nlist_ecoff.c nlist_elf32.c nlist_elf64.c opendir.c \ - pause.c popen.c psignal.c pthread_atfork.c pwcache.c pw_scan.c \ - raise.c randomid.c readdir.c rewinddir.c \ - scandir.c seekdir.c setdomainname.c \ + __opendir30.c pause.c popen.c psignal.c pthread_atfork.c pwcache.c \ + pw_scan.c raise.c randomid.c readdir.c __readdir30.c rewinddir.c \ + scandir.c __scandir30.c seekdir.c setdomainname.c \ sethostname.c setjmperr.c setmode.c setproctitle.c setprogname.c \ shquote.c shquotev.c sighold.c sigignore.c siginterrupt.c \ __siglist14.c siglist.c signal.c __signame14.c signame.c sigrelse.c \ diff --git a/lib/libc/gen/__fts13.c b/lib/libc/gen/__fts13.c index 3b3e11bb1595..3b7bf295bbe2 100644 --- a/lib/libc/gen/__fts13.c +++ b/lib/libc/gen/__fts13.c @@ -1,1224 +1,46 @@ -/* $NetBSD: __fts13.c,v 1.44 2005/01/19 00:59:48 mycroft Exp $ */ - -/*- - * Copyright (c) 1990, 1993, 1994 - * 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. - * 3. 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 HAVE_NBTOOL_CONFIG_H -#include "nbtool_config.h" -#endif - -#include -#if defined(LIBC_SCCS) && !defined(lint) -#if 0 -static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; -#else -__RCSID("$NetBSD: __fts13.c,v 1.44 2005/01/19 00:59:48 mycroft Exp $"); -#endif -#endif /* LIBC_SCCS and not lint */ +/* $NetBSD: __fts13.c,v 1.45 2005/08/19 02:04:54 christos Exp $ */ #include "namespace.h" -#include +#include +#include + +#define __LIBC12_SOURCE__ + +__warn_references(__fts_children13, + "warning: reference to compatibility __fts_children13();" + " include for correct reference") +__warn_references(__fts_close13, + "warning: reference to compatibility __fts_close13();" + " include for correct reference") +__warn_references(__fts_open13, + "warning: reference to compatibility __fts_open13();" + " include for correct reference") +__warn_references(__fts_read13, + "warning: reference to compatibility __fts_read13();" + " include for correct reference") +__warn_references(__fts_set13, + "warning: reference to compatibility __fts_set13();" + " include for correct reference") + #include -#include -#include -#include -#include -#include -#include -#include -#include - -#if ! HAVE_NBTOOL_CONFIG_H -#define HAVE_STRUCT_DIRENT_D_NAMLEN 1 -#endif - -#ifdef __weak_alias -#ifdef __LIBC12_SOURCE__ -__weak_alias(fts_children,_fts_children) -__weak_alias(fts_close,_fts_close) -__weak_alias(fts_open,_fts_open) -__weak_alias(fts_read,_fts_read) -__weak_alias(fts_set,_fts_set) -#endif /* __LIBC12_SOURCE__ */ -#endif /* __weak_alias */ - -#ifdef __LIBC12_SOURCE__ -#define STAT stat12 -#else -#define STAT stat -#endif - -#ifdef __LIBC12_SOURCE__ -__warn_references(fts_children, - "warning: reference to compatibility fts_children();" - " include for correct reference") -__warn_references(fts_close, - "warning: reference to compatibility fts_close();" - " include for correct reference") -__warn_references(fts_open, - "warning: reference to compatibility fts_open();" - " include for correct reference") -__warn_references(fts_read, - "warning: reference to compatibility fts_read();" - " include for correct reference") -__warn_references(fts_set, - "warning: reference to compatibility fts_set();" - " include for correct reference") -#endif - -static FTSENT *fts_alloc __P((FTS *, const char *, size_t)); -static FTSENT *fts_build __P((FTS *, int)); -static void fts_lfree __P((FTSENT *)); -static void fts_load __P((FTS *, FTSENT *)); -static size_t fts_maxarglen __P((char * const *)); -static size_t fts_pow2 __P((size_t)); -static int fts_palloc __P((FTS *, size_t)); -static void fts_padjust __P((FTS *, FTSENT *)); -static FTSENT *fts_sort __P((FTS *, FTSENT *, size_t)); -static u_short fts_stat __P((FTS *, FTSENT *, int)); -static int fts_safe_changedir __P((const FTS *, const FTSENT *, int, - const char *)); - -#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) - -#define CLR(opt) (sp->fts_options &= ~(opt)) -#define ISSET(opt) (sp->fts_options & (opt)) -#define SET(opt) (sp->fts_options |= (opt)) - -#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) -#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) - -/* fts_build flags */ -#define BCHILD 1 /* fts_children */ -#define BNAMES 2 /* fts_children, names only */ -#define BREAD 3 /* fts_read */ - -#ifndef DTF_HIDEW -#undef FTS_WHITEOUT -#endif - -FTS * -fts_open(argv, options, compar) - char * const *argv; - int options; - int (*compar) __P((const FTSENT **, const FTSENT **)); -{ - FTS *sp; - FTSENT *p, *root; - size_t nitems; - FTSENT *parent, *tmp = NULL; /* pacify gcc */ - size_t len; - - _DIAGASSERT(argv != NULL); - - /* Options check. */ - if (options & ~FTS_OPTIONMASK) { - errno = EINVAL; - return (NULL); - } - - /* Allocate/initialize the stream */ - if ((sp = malloc((u_int)sizeof(FTS))) == NULL) - return (NULL); - memset(sp, 0, sizeof(FTS)); - sp->fts_compar = compar; - sp->fts_options = options; - - /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ - if (ISSET(FTS_LOGICAL)) - SET(FTS_NOCHDIR); - - /* - * Start out with 1K of path space, and enough, in any case, - * to hold the user's paths. - */ - if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) - goto mem1; - - /* Allocate/initialize root's parent. */ - if ((parent = fts_alloc(sp, "", 0)) == NULL) - goto mem2; - parent->fts_level = FTS_ROOTPARENTLEVEL; - - /* Allocate/initialize root(s). */ - for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { - /* Don't allow zero-length paths. */ - if ((len = strlen(*argv)) == 0) { - errno = ENOENT; - goto mem3; - } - - if ((p = fts_alloc(sp, *argv, len)) == NULL) - goto mem3; - p->fts_level = FTS_ROOTLEVEL; - p->fts_parent = parent; - p->fts_accpath = p->fts_name; - p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); - - /* Command-line "." and ".." are real directories. */ - if (p->fts_info == FTS_DOT) - p->fts_info = FTS_D; - - /* - * If comparison routine supplied, traverse in sorted - * order; otherwise traverse in the order specified. - */ - if (compar) { - p->fts_link = root; - root = p; - } else { - p->fts_link = NULL; - if (root == NULL) - tmp = root = p; - else { - tmp->fts_link = p; - tmp = p; - } - } - } - if (compar && nitems > 1) - root = fts_sort(sp, root, nitems); - - /* - * Allocate a dummy pointer and make fts_read think that we've just - * finished the node before the root(s); set p->fts_info to FTS_INIT - * so that everything about the "current" node is ignored. - */ - if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) - goto mem3; - sp->fts_cur->fts_link = root; - sp->fts_cur->fts_info = FTS_INIT; - - /* - * If using chdir(2), grab a file descriptor pointing to dot to insure - * that we can get back here; this could be avoided for some paths, - * but almost certainly not worth the effort. Slashes, symbolic links, - * and ".." are all fairly nasty problems. Note, if we can't get the - * descriptor we run anyway, just more slowly. - */ - if (!ISSET(FTS_NOCHDIR)) { - if ((sp->fts_rfd = open(".", O_RDONLY, 0)) == -1) - SET(FTS_NOCHDIR); - else if (fcntl(sp->fts_rfd, F_SETFD, FD_CLOEXEC) == -1) { - close(sp->fts_rfd); - SET(FTS_NOCHDIR); - } - } - - return (sp); - -mem3: fts_lfree(root); - free(parent); -mem2: free(sp->fts_path); -mem1: free(sp); - return (NULL); -} - -static void -fts_load(sp, p) - FTS *sp; - FTSENT *p; -{ - size_t len; - char *cp; - - _DIAGASSERT(sp != NULL); - _DIAGASSERT(p != NULL); - - /* - * Load the stream structure for the next traversal. Since we don't - * actually enter the directory until after the preorder visit, set - * the fts_accpath field specially so the chdir gets done to the right - * place and the user can access the first node. From fts_open it's - * known that the path will fit. - */ - len = p->fts_pathlen = p->fts_namelen; - memmove(sp->fts_path, p->fts_name, len + 1); - if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { - len = strlen(++cp); - memmove(p->fts_name, cp, len + 1); - p->fts_namelen = len; - } - p->fts_accpath = p->fts_path = sp->fts_path; - sp->fts_dev = p->fts_dev; -} - -int -fts_close(sp) - FTS *sp; -{ - FTSENT *freep, *p; - int saved_errno = 0; - - _DIAGASSERT(sp != NULL); - - /* - * This still works if we haven't read anything -- the dummy structure - * points to the root list, so we step through to the end of the root - * list which has a valid parent pointer. - */ - if (sp->fts_cur) { - if (ISSET(FTS_SYMFOLLOW)) - (void)close(sp->fts_cur->fts_symfd); - for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { - freep = p; - p = p->fts_link ? p->fts_link : p->fts_parent; - free(freep); - } - free(p); - } - - /* Free up child linked list, sort array, path buffer. */ - if (sp->fts_child) - fts_lfree(sp->fts_child); - if (sp->fts_array) - free(sp->fts_array); - free(sp->fts_path); - - /* Return to original directory, save errno if necessary. */ - if (!ISSET(FTS_NOCHDIR)) { - if (fchdir(sp->fts_rfd)) - saved_errno = errno; - (void)close(sp->fts_rfd); - } - - /* Free up the stream pointer. */ - free(sp); - /* ISSET() is illegal after this, since the macro touches sp */ - - /* Set errno and return. */ - if (saved_errno) { - errno = saved_errno; - return (-1); - } - return (0); -} - -/* - * Special case a root of "/" so that slashes aren't appended which would - * cause paths to be written as "//foo". - */ -#define NAPPEND(p) \ - (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ - p->fts_path[0] == '/' ? 0 : p->fts_pathlen) - -FTSENT * -fts_read(sp) - FTS *sp; -{ - FTSENT *p, *tmp; - int instr; - char *t; - int saved_errno; - - _DIAGASSERT(sp != NULL); - - /* If finished or unrecoverable error, return NULL. */ - if (sp->fts_cur == NULL || ISSET(FTS_STOP)) - return (NULL); - - /* Set current node pointer. */ - p = sp->fts_cur; - - /* Save and zero out user instructions. */ - instr = p->fts_instr; - p->fts_instr = FTS_NOINSTR; - - /* Any type of file may be re-visited; re-stat and re-turn. */ - if (instr == FTS_AGAIN) { - p->fts_info = fts_stat(sp, p, 0); - return (p); - } - - /* - * Following a symlink -- SLNONE test allows application to see - * SLNONE and recover. If indirecting through a symlink, have - * keep a pointer to current location. If unable to get that - * pointer, follow fails. - */ - if (instr == FTS_FOLLOW && - (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { - p->fts_info = fts_stat(sp, p, 1); - if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { - if ((p->fts_symfd = open(".", O_RDONLY, 0)) == -1) { - p->fts_errno = errno; - p->fts_info = FTS_ERR; - } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) { - p->fts_errno = errno; - p->fts_info = FTS_ERR; - close(p->fts_symfd); - } else - p->fts_flags |= FTS_SYMFOLLOW; - } - return (p); - } - - /* Directory in pre-order. */ - if (p->fts_info == FTS_D) { - /* If skipped or crossed mount point, do post-order visit. */ - if (instr == FTS_SKIP || - (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { - if (p->fts_flags & FTS_SYMFOLLOW) - (void)close(p->fts_symfd); - if (sp->fts_child) { - fts_lfree(sp->fts_child); - sp->fts_child = NULL; - } - p->fts_info = FTS_DP; - return (p); - } - - /* Rebuild if only read the names and now traversing. */ - if (sp->fts_child && ISSET(FTS_NAMEONLY)) { - CLR(FTS_NAMEONLY); - fts_lfree(sp->fts_child); - sp->fts_child = NULL; - } - - /* - * Cd to the subdirectory. - * - * If have already read and now fail to chdir, whack the list - * to make the names come out right, and set the parent errno - * so the application will eventually get an error condition. - * Set the FTS_DONTCHDIR flag so that when we logically change - * directories back to the parent we don't do a chdir. - * - * If haven't read do so. If the read fails, fts_build sets - * FTS_STOP or the fts_info field of the node. - */ - if (sp->fts_child) { - if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { - p->fts_errno = errno; - p->fts_flags |= FTS_DONTCHDIR; - for (p = sp->fts_child; p; p = p->fts_link) - p->fts_accpath = - p->fts_parent->fts_accpath; - } - } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { - if (ISSET(FTS_STOP)) - return (NULL); - return (p); - } - p = sp->fts_child; - sp->fts_child = NULL; - goto name; - } - - /* Move to the next node on this level. */ -next: tmp = p; - if ((p = p->fts_link) != NULL) { - free(tmp); - - /* - * If reached the top, return to the original directory, and - * load the paths for the next root. - */ - if (p->fts_level == FTS_ROOTLEVEL) { - if (FCHDIR(sp, sp->fts_rfd)) { - SET(FTS_STOP); - return (NULL); - } - fts_load(sp, p); - return (sp->fts_cur = p); - } - - /* - * User may have called fts_set on the node. If skipped, - * ignore. If followed, get a file descriptor so we can - * get back if necessary. - */ - if (p->fts_instr == FTS_SKIP) - goto next; - if (p->fts_instr == FTS_FOLLOW) { - p->fts_info = fts_stat(sp, p, 1); - if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { - if ((p->fts_symfd = - open(".", O_RDONLY, 0)) == -1) { - p->fts_errno = errno; - p->fts_info = FTS_ERR; - } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) { - p->fts_errno = errno; - p->fts_info = FTS_ERR; - close(p->fts_symfd); - } else - p->fts_flags |= FTS_SYMFOLLOW; - } - p->fts_instr = FTS_NOINSTR; - } - -name: t = sp->fts_path + NAPPEND(p->fts_parent); - *t++ = '/'; - memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1)); - return (sp->fts_cur = p); - } - - /* Move up to the parent node. */ - p = tmp->fts_parent; - free(tmp); - - if (p->fts_level == FTS_ROOTPARENTLEVEL) { - /* - * Done; free everything up and set errno to 0 so the user - * can distinguish between error and EOF. - */ - free(p); - errno = 0; - return (sp->fts_cur = NULL); - } - - /* Nul terminate the pathname. */ - sp->fts_path[p->fts_pathlen] = '\0'; - - /* - * Return to the parent directory. If at a root node or came through - * a symlink, go back through the file descriptor. Otherwise, cd up - * one directory. - */ - if (p->fts_level == FTS_ROOTLEVEL) { - if (FCHDIR(sp, sp->fts_rfd)) { - SET(FTS_STOP); - return (NULL); - } - } else if (p->fts_flags & FTS_SYMFOLLOW) { - if (FCHDIR(sp, p->fts_symfd)) { - saved_errno = errno; - (void)close(p->fts_symfd); - errno = saved_errno; - SET(FTS_STOP); - return (NULL); - } - (void)close(p->fts_symfd); - } else if (!(p->fts_flags & FTS_DONTCHDIR) && - fts_safe_changedir(sp, p->fts_parent, -1, "..")) { - SET(FTS_STOP); - return (NULL); - } - p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; - return (sp->fts_cur = p); -} - -/* - * Fts_set takes the stream as an argument although it's not used in this - * implementation; it would be necessary if anyone wanted to add global - * semantics to fts using fts_set. An error return is allowed for similar - * reasons. - */ -/* ARGSUSED */ -int -fts_set(sp, p, instr) - FTS *sp; - FTSENT *p; - int instr; -{ - - _DIAGASSERT(sp != NULL); - _DIAGASSERT(p != NULL); - - if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && - instr != FTS_NOINSTR && instr != FTS_SKIP) { - errno = EINVAL; - return (1); - } - p->fts_instr = instr; - return (0); -} - -FTSENT * -fts_children(sp, instr) - FTS *sp; - int instr; -{ - FTSENT *p; - int fd; - - _DIAGASSERT(sp != NULL); - - if (instr && instr != FTS_NAMEONLY) { - errno = EINVAL; - return (NULL); - } - - /* Set current node pointer. */ - p = sp->fts_cur; - - /* - * Errno set to 0 so user can distinguish empty directory from - * an error. - */ - errno = 0; - - /* Fatal errors stop here. */ - if (ISSET(FTS_STOP)) - return (NULL); - - /* Return logical hierarchy of user's arguments. */ - if (p->fts_info == FTS_INIT) - return (p->fts_link); - - /* - * If not a directory being visited in pre-order, stop here. Could - * allow FTS_DNR, assuming the user has fixed the problem, but the - * same effect is available with FTS_AGAIN. - */ - if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) - return (NULL); - - /* Free up any previous child list. */ - if (sp->fts_child) - fts_lfree(sp->fts_child); - - if (instr == FTS_NAMEONLY) { - SET(FTS_NAMEONLY); - instr = BNAMES; - } else - instr = BCHILD; - - /* - * If using chdir on a relative path and called BEFORE fts_read does - * its chdir to the root of a traversal, we can lose -- we need to - * chdir into the subdirectory, and we don't know where the current - * directory is, so we can't get back so that the upcoming chdir by - * fts_read will work. - */ - if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || - ISSET(FTS_NOCHDIR)) - return (sp->fts_child = fts_build(sp, instr)); - - if ((fd = open(".", O_RDONLY, 0)) == -1) - return (sp->fts_child = NULL); - sp->fts_child = fts_build(sp, instr); - if (fchdir(fd)) { - (void)close(fd); - return (NULL); - } - (void)close(fd); - return (sp->fts_child); -} - -/* - * This is the tricky part -- do not casually change *anything* in here. The - * idea is to build the linked list of entries that are used by fts_children - * and fts_read. There are lots of special cases. - * - * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is - * set and it's a physical walk (so that symbolic links can't be directories), - * we can do things quickly. First, if it's a 4.4BSD file system, the type - * of the file is in the directory entry. Otherwise, we assume that the number - * of subdirectories in a node is equal to the number of links to the parent. - * The former skips all stat calls. The latter skips stat calls in any leaf - * directories and for any files after the subdirectories in the directory have - * been found, cutting the stat calls by about 2/3. - */ -static FTSENT * -fts_build(sp, type) - FTS *sp; - int type; -{ - struct dirent *dp; - FTSENT *p, *head; - size_t nitems; - FTSENT *cur, *tail; - DIR *dirp; - int adjust, cderrno, descend, len, level, nlinks, saved_errno, nostat; - size_t maxlen; -#ifdef FTS_WHITEOUT - int oflag; -#endif - char *cp = NULL; /* pacify gcc */ - - _DIAGASSERT(sp != NULL); - - /* Set current node pointer. */ - cur = sp->fts_cur; - - /* - * Open the directory for reading. If this fails, we're done. - * If being called from fts_read, set the fts_info field. - */ -#ifdef FTS_WHITEOUT - if (ISSET(FTS_WHITEOUT)) - oflag = DTF_NODUP|DTF_REWIND; - else - oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; -#else -#define __opendir2(path, flag) opendir(path) -#endif - if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { - if (type == BREAD) { - cur->fts_info = FTS_DNR; - cur->fts_errno = errno; - } - return (NULL); - } - - /* - * Nlinks is the number of possible entries of type directory in the - * directory if we're cheating on stat calls, 0 if we're not doing - * any stat calls at all, -1 if we're doing stats on everything. - */ - if (type == BNAMES) { - nlinks = 0; - nostat = 1; - } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { - nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); - nostat = 1; - } else { - nlinks = -1; - nostat = 0; - } - -#ifdef notdef - (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); - (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", - ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); -#endif - /* - * If we're going to need to stat anything or we want to descend - * and stay in the directory, chdir. If this fails we keep going, - * but set a flag so we don't chdir after the post-order visit. - * We won't be able to stat anything, but we can still return the - * names themselves. Note, that since fts_read won't be able to - * chdir into the directory, it will have to return different path - * names than before, i.e. "a/b" instead of "b". Since the node - * has already been visited in pre-order, have to wait until the - * post-order visit to return the error. There is a special case - * here, if there was nothing to stat then it's not an error to - * not be able to stat. This is all fairly nasty. If a program - * needed sorted entries or stat information, they had better be - * checking FTS_NS on the returned nodes. - */ - cderrno = 0; - if (nlinks || type == BREAD) { - if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { - if (nlinks && type == BREAD) - cur->fts_errno = errno; - cur->fts_flags |= FTS_DONTCHDIR; - descend = 0; - cderrno = errno; - } else - descend = 1; - } else - descend = 0; - - /* - * Figure out the max file name length that can be stored in the - * current path -- the inner loop allocates more path as necessary. - * We really wouldn't have to do the maxlen calculations here, we - * could do them in fts_read before returning the path, but it's a - * lot easier here since the length is part of the dirent structure. - * - * If not changing directories set a pointer so that can just append - * each new name into the path. - */ - len = NAPPEND(cur); - if (ISSET(FTS_NOCHDIR)) { - cp = sp->fts_path + len; - *cp++ = '/'; - } - len++; - maxlen = sp->fts_pathlen - len; - - level = cur->fts_level + 1; - - /* Read the directory, attaching each entry to the `link' pointer. */ - adjust = 0; - for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) { - size_t dlen; - - if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) - continue; - -#if HAVE_STRUCT_DIRENT_D_NAMLEN - dlen = dp->d_namlen; -#else - dlen = strlen(dp->d_name); -#endif - if ((p = fts_alloc(sp, dp->d_name, dlen)) == NULL) - goto mem1; - if (dlen >= maxlen) { /* include space for NUL */ - if (fts_palloc(sp, len + dlen + 1)) { - /* - * No more memory for path or structures. Save - * errno, free up the current structure and the - * structures already allocated. - */ -mem1: saved_errno = errno; - if (p) - free(p); - fts_lfree(head); - (void)closedir(dirp); - errno = saved_errno; - cur->fts_info = FTS_ERR; - SET(FTS_STOP); - return (NULL); - } - adjust = 1; - if (ISSET(FTS_NOCHDIR)) - cp = sp->fts_path + len; - maxlen = sp->fts_pathlen - len; - } - - p->fts_pathlen = len + dlen; - p->fts_parent = sp->fts_cur; - p->fts_level = level; - -#ifdef FTS_WHITEOUT - if (dp->d_type == DT_WHT) - p->fts_flags |= FTS_ISW; -#endif - - if (cderrno) { - if (nlinks) { - p->fts_info = FTS_NS; - p->fts_errno = cderrno; - } else - p->fts_info = FTS_NSOK; - p->fts_accpath = cur->fts_accpath; - } else if (nlinks == 0 -#ifdef DT_DIR - || (nostat && - dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) -#endif - ) { - p->fts_accpath = - ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; - p->fts_info = FTS_NSOK; - } else { - /* Build a file name for fts_stat to stat. */ - if (ISSET(FTS_NOCHDIR)) { - p->fts_accpath = p->fts_path; - memmove(cp, p->fts_name, - (size_t)(p->fts_namelen + 1)); - } else - p->fts_accpath = p->fts_name; - /* Stat it. */ - p->fts_info = fts_stat(sp, p, 0); - - /* Decrement link count if applicable. */ - if (nlinks > 0 && (p->fts_info == FTS_D || - p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) - --nlinks; - } - - /* We walk in directory order so "ls -f" doesn't get upset. */ - p->fts_link = NULL; - if (head == NULL) - head = tail = p; - else { - tail->fts_link = p; - tail = p; - } - ++nitems; - } - (void)closedir(dirp); - - /* - * If had to realloc the path, adjust the addresses for the rest - * of the tree. - */ - if (adjust) - fts_padjust(sp, head); - - /* - * If not changing directories, reset the path back to original - * state. - */ - if (ISSET(FTS_NOCHDIR)) { - if (cp - 1 > sp->fts_path) - --cp; - *cp = '\0'; - } - - /* - * If descended after called from fts_children or after called from - * fts_read and nothing found, get back. At the root level we use - * the saved fd; if one of fts_open()'s arguments is a relative path - * to an empty directory, we wind up here with no other way back. If - * can't get back, we're done. - */ - if (descend && (type == BCHILD || !nitems) && - (cur->fts_level == FTS_ROOTLEVEL ? - FCHDIR(sp, sp->fts_rfd) : - fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { - cur->fts_info = FTS_ERR; - SET(FTS_STOP); - return (NULL); - } - - /* If didn't find anything, return NULL. */ - if (!nitems) { - if (type == BREAD) - cur->fts_info = FTS_DP; - return (NULL); - } - - /* Sort the entries. */ - if (sp->fts_compar && nitems > 1) - head = fts_sort(sp, head, nitems); - return (head); -} - -static u_short -fts_stat(sp, p, follow) - FTS *sp; - FTSENT *p; - int follow; -{ - FTSENT *t; - dev_t dev; - ino_t ino; - struct STAT *sbp, sb; - int saved_errno; - - _DIAGASSERT(sp != NULL); - _DIAGASSERT(p != NULL); - - /* If user needs stat info, stat buffer already allocated. */ - sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; - -#ifdef FTS_WHITEOUT - /* check for whiteout */ - if (p->fts_flags & FTS_ISW) { - if (sbp != &sb) { - memset(sbp, '\0', sizeof (*sbp)); - sbp->st_mode = S_IFWHT; - } - return (FTS_W); - } -#endif - - /* - * If doing a logical walk, or application requested FTS_FOLLOW, do - * a stat(2). If that fails, check for a non-existent symlink. If - * fail, set the errno from the stat call. - */ - if (ISSET(FTS_LOGICAL) || follow) { - if (stat(p->fts_accpath, sbp)) { - saved_errno = errno; - if (!lstat(p->fts_accpath, sbp)) { - errno = 0; - return (FTS_SLNONE); - } - p->fts_errno = saved_errno; - goto err; - } - } else if (lstat(p->fts_accpath, sbp)) { - p->fts_errno = errno; -err: memset(sbp, 0, sizeof(struct STAT)); - return (FTS_NS); - } - - if (S_ISDIR(sbp->st_mode)) { - /* - * Set the device/inode. Used to find cycles and check for - * crossing mount points. Also remember the link count, used - * in fts_build to limit the number of stat calls. It is - * understood that these fields are only referenced if fts_info - * is set to FTS_D. - */ - dev = p->fts_dev = sbp->st_dev; - ino = p->fts_ino = sbp->st_ino; - p->fts_nlink = sbp->st_nlink; - - if (ISDOT(p->fts_name)) - return (FTS_DOT); - - /* - * Cycle detection is done by brute force when the directory - * is first encountered. If the tree gets deep enough or the - * number of symbolic links to directories is high enough, - * something faster might be worthwhile. - */ - for (t = p->fts_parent; - t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) - if (ino == t->fts_ino && dev == t->fts_dev) { - p->fts_cycle = t; - return (FTS_DC); - } - return (FTS_D); - } - if (S_ISLNK(sbp->st_mode)) - return (FTS_SL); - if (S_ISREG(sbp->st_mode)) - return (FTS_F); - return (FTS_DEFAULT); -} - -static FTSENT * -fts_sort(sp, head, nitems) - FTS *sp; - FTSENT *head; - size_t nitems; -{ - FTSENT **ap, *p; - - _DIAGASSERT(sp != NULL); - _DIAGASSERT(head != NULL); - - /* - * Construct an array of pointers to the structures and call qsort(3). - * Reassemble the array in the order returned by qsort. If unable to - * sort for memory reasons, return the directory entries in their - * current order. Allocate enough space for the current needs plus - * 40 so don't realloc one entry at a time. - */ - if (nitems > sp->fts_nitems) { - FTSENT **new; - - new = realloc(sp->fts_array, sizeof(FTSENT *) * (nitems + 40)); - if (new == 0) - return (head); - sp->fts_array = new; - sp->fts_nitems = nitems + 40; - } - for (ap = sp->fts_array, p = head; p; p = p->fts_link) - *ap++ = p; - qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), - (int (*) __P((const void *, const void *)))sp->fts_compar); - for (head = *(ap = sp->fts_array); --nitems; ++ap) - ap[0]->fts_link = ap[1]; - ap[0]->fts_link = NULL; - return (head); -} - -static FTSENT * -fts_alloc(sp, name, namelen) - FTS *sp; - const char *name; - size_t namelen; -{ - FTSENT *p; - size_t len; - - _DIAGASSERT(sp != NULL); - _DIAGASSERT(name != NULL); - -#if defined(ALIGNBYTES) && defined(ALIGN) - /* - * The file name is a variable length array and no stat structure is - * necessary if the user has set the nostat bit. Allocate the FTSENT - * structure, the file name and the stat structure in one chunk, but - * be careful that the stat structure is reasonably aligned. Since the - * fts_name field is declared to be of size 1, the fts_name pointer is - * namelen + 2 before the first possible address of the stat structure. - */ - len = sizeof(FTSENT) + namelen; - if (!ISSET(FTS_NOSTAT)) - len += sizeof(struct STAT) + ALIGNBYTES; - if ((p = malloc(len)) == NULL) - return (NULL); - - if (!ISSET(FTS_NOSTAT)) - p->fts_statp = - (struct STAT *)ALIGN((u_long)(p->fts_name + namelen + 2)); -#else - if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL) - return (NULL); - - if (!ISSET(FTS_NOSTAT)) - if ((p->fts_statp = malloc(sizeof(struct STAT))) == NULL) { - free(p); - return (NULL); - } -#endif - - /* Copy the name plus the trailing NULL. */ - memmove(p->fts_name, name, namelen + 1); - - p->fts_namelen = namelen; - p->fts_path = sp->fts_path; - p->fts_errno = 0; - p->fts_flags = 0; - p->fts_instr = FTS_NOINSTR; - p->fts_number = 0; - p->fts_pointer = NULL; - return (p); -} - -static void -fts_lfree(head) - FTSENT *head; -{ - FTSENT *p; - - /* XXX: head may be NULL ? */ - - /* Free a linked list of structures. */ - while ((p = head) != NULL) { - head = head->fts_link; - -#if !defined(ALIGNBYTES) || !defined(ALIGN) - if (p->fts_statp) - free(p->fts_statp); -#endif - free(p); - } -} - -static size_t -fts_pow2(x) - size_t x; -{ - - x--; - x |= x>>1; - x |= x>>2; - x |= x>>4; - x |= x>>8; - x |= x>>16; -#if LONG_BIT > 32 - x |= x>>32; -#endif -#if LONG_BIT > 64 - x |= x>>64; -#endif - x++; - return (x); -} - -/* - * Allow essentially unlimited paths; find, rm, ls should all work on any tree. - * Most systems will allow creation of paths much longer than MAXPATHLEN, even - * though the kernel won't resolve them. Round up the new size to a power of 2, - * so we don't realloc the path 2 bytes at a time. - */ -static int -fts_palloc(sp, size) - FTS *sp; - size_t size; -{ - char *new; - - _DIAGASSERT(sp != NULL); - -#if 1 - /* Protect against fts_pathlen overflow. */ - if (size > USHRT_MAX + 1) { - errno = ENOMEM; - return (1); - } -#endif - size = fts_pow2(size); - new = realloc(sp->fts_path, size); - if (new == 0) - return (1); - sp->fts_path = new; - sp->fts_pathlen = size; - return (0); -} - -/* - * When the path is realloc'd, have to fix all of the pointers in structures - * already returned. - */ -static void -fts_padjust(sp, head) - FTS *sp; - FTSENT *head; -{ - FTSENT *p; - char *addr; - - _DIAGASSERT(sp != NULL); - -#define ADJUST(p) do { \ - if ((p)->fts_accpath != (p)->fts_name) \ - (p)->fts_accpath = \ - addr + ((p)->fts_accpath - (p)->fts_path); \ - (p)->fts_path = addr; \ -} while (/*CONSTCOND*/0) - - addr = sp->fts_path; - - /* Adjust the current set of children. */ - for (p = sp->fts_child; p; p = p->fts_link) - ADJUST(p); - - /* Adjust the rest of the tree, including the current level. */ - for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { - ADJUST(p); - p = p->fts_link ? p->fts_link : p->fts_parent; - } -} - -static size_t -fts_maxarglen(argv) - char * const *argv; -{ - size_t len, max; - - _DIAGASSERT(argv != NULL); - - for (max = 0; *argv; ++argv) - if ((len = strlen(*argv)) > max) - max = len; - return (max + 1); -} - -/* - * Change to dir specified by fd or p->fts_accpath without getting - * tricked by someone changing the world out from underneath us. - * Assumes p->fts_dev and p->fts_ino are filled in. - */ -static int -fts_safe_changedir(sp, p, fd, path) - const FTS *sp; - const FTSENT *p; - int fd; - const char *path; -{ - int oldfd = fd, ret = -1; - struct STAT sb; - - if (ISSET(FTS_NOCHDIR)) - return 0; - - if (oldfd < 0 && (fd = open(path, O_RDONLY)) == -1) - return -1; - - if (fstat(fd, &sb) == -1) - goto bail; - - if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) { - errno = ENOENT; - goto bail; - } - - ret = fchdir(fd); - -bail: - if (oldfd < 0) { - int save_errno = errno; - (void)close(fd); - errno = save_errno; - } - return ret; -} +#define stat __stat13 +#define lstat __lstat13 +#define fstat __fstat13 + +#define __fts_stat_t struct stat13 +#define __fts_nlink_t nlink_t +#define __fts_ino_t u_int32_t + +#undef fts_children +#define fts_children __fts_children13 +#undef fts_close +#define fts_close __fts_close13 +#undef fts_open +#define fts_open __fts_open13 +#undef fts_read +#define fts_read __fts_read13 +#undef fts_set +#define fts_set __fts_set13 + +#include "__fts30.c" diff --git a/lib/libc/gen/__fts30.c b/lib/libc/gen/__fts30.c new file mode 100644 index 000000000000..3f99f7faa886 --- /dev/null +++ b/lib/libc/gen/__fts30.c @@ -0,0 +1,1190 @@ +/* $NetBSD: __fts30.c,v 1.1 2005/08/19 02:04:54 christos Exp $ */ + +/*- + * Copyright (c) 1990, 1993, 1994 + * 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. + * 3. 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 HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; +#else +__RCSID("$NetBSD: __fts30.c,v 1.1 2005/08/19 02:04:54 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include "namespace.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if ! HAVE_NBTOOL_CONFIG_H +#define HAVE_STRUCT_DIRENT_D_NAMLEN 1 +#endif + +static FTSENT *fts_alloc __P((FTS *, const char *, size_t)); +static FTSENT *fts_build __P((FTS *, int)); +static void fts_lfree __P((FTSENT *)); +static void fts_load __P((FTS *, FTSENT *)); +static size_t fts_maxarglen __P((char * const *)); +static size_t fts_pow2 __P((size_t)); +static int fts_palloc __P((FTS *, size_t)); +static void fts_padjust __P((FTS *, FTSENT *)); +static FTSENT *fts_sort __P((FTS *, FTSENT *, size_t)); +static u_short fts_stat __P((FTS *, FTSENT *, int)); +static int fts_safe_changedir __P((const FTS *, const FTSENT *, int, + const char *)); + +#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) + +#define CLR(opt) (sp->fts_options &= ~(opt)) +#define ISSET(opt) (sp->fts_options & (opt)) +#define SET(opt) (sp->fts_options |= (opt)) + +#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) +#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) + +/* fts_build flags */ +#define BCHILD 1 /* fts_children */ +#define BNAMES 2 /* fts_children, names only */ +#define BREAD 3 /* fts_read */ + +#ifndef DTF_HIDEW +#undef FTS_WHITEOUT +#endif + +FTS * +fts_open(argv, options, compar) + char * const *argv; + int options; + int (*compar) __P((const FTSENT **, const FTSENT **)); +{ + FTS *sp; + FTSENT *p, *root; + size_t nitems; + FTSENT *parent, *tmp = NULL; /* pacify gcc */ + size_t len; + + _DIAGASSERT(argv != NULL); + + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return (NULL); + } + + /* Allocate/initialize the stream */ + if ((sp = malloc((u_int)sizeof(FTS))) == NULL) + return (NULL); + memset(sp, 0, sizeof(FTS)); + sp->fts_compar = compar; + sp->fts_options = options; + + /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ + if (ISSET(FTS_LOGICAL)) + SET(FTS_NOCHDIR); + + /* + * Start out with 1K of path space, and enough, in any case, + * to hold the user's paths. + */ + if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) + goto mem1; + + /* Allocate/initialize root's parent. */ + if ((parent = fts_alloc(sp, "", 0)) == NULL) + goto mem2; + parent->fts_level = FTS_ROOTPARENTLEVEL; + + /* Allocate/initialize root(s). */ + for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { + /* Don't allow zero-length paths. */ + if ((len = strlen(*argv)) == 0) { + errno = ENOENT; + goto mem3; + } + + if ((p = fts_alloc(sp, *argv, len)) == NULL) + goto mem3; + p->fts_level = FTS_ROOTLEVEL; + p->fts_parent = parent; + p->fts_accpath = p->fts_name; + p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); + + /* Command-line "." and ".." are real directories. */ + if (p->fts_info == FTS_DOT) + p->fts_info = FTS_D; + + /* + * If comparison routine supplied, traverse in sorted + * order; otherwise traverse in the order specified. + */ + if (compar) { + p->fts_link = root; + root = p; + } else { + p->fts_link = NULL; + if (root == NULL) + tmp = root = p; + else { + tmp->fts_link = p; + tmp = p; + } + } + } + if (compar && nitems > 1) + root = fts_sort(sp, root, nitems); + + /* + * Allocate a dummy pointer and make fts_read think that we've just + * finished the node before the root(s); set p->fts_info to FTS_INIT + * so that everything about the "current" node is ignored. + */ + if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) + goto mem3; + sp->fts_cur->fts_link = root; + sp->fts_cur->fts_info = FTS_INIT; + + /* + * If using chdir(2), grab a file descriptor pointing to dot to insure + * that we can get back here; this could be avoided for some paths, + * but almost certainly not worth the effort. Slashes, symbolic links, + * and ".." are all fairly nasty problems. Note, if we can't get the + * descriptor we run anyway, just more slowly. + */ + if (!ISSET(FTS_NOCHDIR)) { + if ((sp->fts_rfd = open(".", O_RDONLY, 0)) == -1) + SET(FTS_NOCHDIR); + else if (fcntl(sp->fts_rfd, F_SETFD, FD_CLOEXEC) == -1) { + close(sp->fts_rfd); + SET(FTS_NOCHDIR); + } + } + + return (sp); + +mem3: fts_lfree(root); + free(parent); +mem2: free(sp->fts_path); +mem1: free(sp); + return (NULL); +} + +static void +fts_load(sp, p) + FTS *sp; + FTSENT *p; +{ + size_t len; + char *cp; + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(p != NULL); + + /* + * Load the stream structure for the next traversal. Since we don't + * actually enter the directory until after the preorder visit, set + * the fts_accpath field specially so the chdir gets done to the right + * place and the user can access the first node. From fts_open it's + * known that the path will fit. + */ + len = p->fts_pathlen = p->fts_namelen; + memmove(sp->fts_path, p->fts_name, len + 1); + if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { + len = strlen(++cp); + memmove(p->fts_name, cp, len + 1); + p->fts_namelen = len; + } + p->fts_accpath = p->fts_path = sp->fts_path; + sp->fts_dev = p->fts_dev; +} + +int +fts_close(sp) + FTS *sp; +{ + FTSENT *freep, *p; + int saved_errno = 0; + + _DIAGASSERT(sp != NULL); + + /* + * This still works if we haven't read anything -- the dummy structure + * points to the root list, so we step through to the end of the root + * list which has a valid parent pointer. + */ + if (sp->fts_cur) { + if (ISSET(FTS_SYMFOLLOW)) + (void)close(sp->fts_cur->fts_symfd); + for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { + freep = p; + p = p->fts_link ? p->fts_link : p->fts_parent; + free(freep); + } + free(p); + } + + /* Free up child linked list, sort array, path buffer. */ + if (sp->fts_child) + fts_lfree(sp->fts_child); + if (sp->fts_array) + free(sp->fts_array); + free(sp->fts_path); + + /* Return to original directory, save errno if necessary. */ + if (!ISSET(FTS_NOCHDIR)) { + if (fchdir(sp->fts_rfd)) + saved_errno = errno; + (void)close(sp->fts_rfd); + } + + /* Free up the stream pointer. */ + free(sp); + /* ISSET() is illegal after this, since the macro touches sp */ + + /* Set errno and return. */ + if (saved_errno) { + errno = saved_errno; + return (-1); + } + return (0); +} + +/* + * Special case a root of "/" so that slashes aren't appended which would + * cause paths to be written as "//foo". + */ +#define NAPPEND(p) \ + (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ + p->fts_path[0] == '/' ? 0 : p->fts_pathlen) + +FTSENT * +fts_read(sp) + FTS *sp; +{ + FTSENT *p, *tmp; + int instr; + char *t; + int saved_errno; + + _DIAGASSERT(sp != NULL); + + /* If finished or unrecoverable error, return NULL. */ + if (sp->fts_cur == NULL || ISSET(FTS_STOP)) + return (NULL); + + /* Set current node pointer. */ + p = sp->fts_cur; + + /* Save and zero out user instructions. */ + instr = p->fts_instr; + p->fts_instr = FTS_NOINSTR; + + /* Any type of file may be re-visited; re-stat and re-turn. */ + if (instr == FTS_AGAIN) { + p->fts_info = fts_stat(sp, p, 0); + return (p); + } + + /* + * Following a symlink -- SLNONE test allows application to see + * SLNONE and recover. If indirecting through a symlink, have + * keep a pointer to current location. If unable to get that + * pointer, follow fails. + */ + if (instr == FTS_FOLLOW && + (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { + p->fts_info = fts_stat(sp, p, 1); + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { + if ((p->fts_symfd = open(".", O_RDONLY, 0)) == -1) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + close(p->fts_symfd); + } else + p->fts_flags |= FTS_SYMFOLLOW; + } + return (p); + } + + /* Directory in pre-order. */ + if (p->fts_info == FTS_D) { + /* If skipped or crossed mount point, do post-order visit. */ + if (instr == FTS_SKIP || + (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { + if (p->fts_flags & FTS_SYMFOLLOW) + (void)close(p->fts_symfd); + if (sp->fts_child) { + fts_lfree(sp->fts_child); + sp->fts_child = NULL; + } + p->fts_info = FTS_DP; + return (p); + } + + /* Rebuild if only read the names and now traversing. */ + if (sp->fts_child && ISSET(FTS_NAMEONLY)) { + CLR(FTS_NAMEONLY); + fts_lfree(sp->fts_child); + sp->fts_child = NULL; + } + + /* + * Cd to the subdirectory. + * + * If have already read and now fail to chdir, whack the list + * to make the names come out right, and set the parent errno + * so the application will eventually get an error condition. + * Set the FTS_DONTCHDIR flag so that when we logically change + * directories back to the parent we don't do a chdir. + * + * If haven't read do so. If the read fails, fts_build sets + * FTS_STOP or the fts_info field of the node. + */ + if (sp->fts_child) { + if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { + p->fts_errno = errno; + p->fts_flags |= FTS_DONTCHDIR; + for (p = sp->fts_child; p; p = p->fts_link) + p->fts_accpath = + p->fts_parent->fts_accpath; + } + } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { + if (ISSET(FTS_STOP)) + return (NULL); + return (p); + } + p = sp->fts_child; + sp->fts_child = NULL; + goto name; + } + + /* Move to the next node on this level. */ +next: tmp = p; + if ((p = p->fts_link) != NULL) { + free(tmp); + + /* + * If reached the top, return to the original directory, and + * load the paths for the next root. + */ + if (p->fts_level == FTS_ROOTLEVEL) { + if (FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + return (NULL); + } + fts_load(sp, p); + return (sp->fts_cur = p); + } + + /* + * User may have called fts_set on the node. If skipped, + * ignore. If followed, get a file descriptor so we can + * get back if necessary. + */ + if (p->fts_instr == FTS_SKIP) + goto next; + if (p->fts_instr == FTS_FOLLOW) { + p->fts_info = fts_stat(sp, p, 1); + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { + if ((p->fts_symfd = + open(".", O_RDONLY, 0)) == -1) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + } else if (fcntl(p->fts_symfd, F_SETFD, FD_CLOEXEC) == -1) { + p->fts_errno = errno; + p->fts_info = FTS_ERR; + close(p->fts_symfd); + } else + p->fts_flags |= FTS_SYMFOLLOW; + } + p->fts_instr = FTS_NOINSTR; + } + +name: t = sp->fts_path + NAPPEND(p->fts_parent); + *t++ = '/'; + memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1)); + return (sp->fts_cur = p); + } + + /* Move up to the parent node. */ + p = tmp->fts_parent; + free(tmp); + + if (p->fts_level == FTS_ROOTPARENTLEVEL) { + /* + * Done; free everything up and set errno to 0 so the user + * can distinguish between error and EOF. + */ + free(p); + errno = 0; + return (sp->fts_cur = NULL); + } + + /* Nul terminate the pathname. */ + sp->fts_path[p->fts_pathlen] = '\0'; + + /* + * Return to the parent directory. If at a root node or came through + * a symlink, go back through the file descriptor. Otherwise, cd up + * one directory. + */ + if (p->fts_level == FTS_ROOTLEVEL) { + if (FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + return (NULL); + } + } else if (p->fts_flags & FTS_SYMFOLLOW) { + if (FCHDIR(sp, p->fts_symfd)) { + saved_errno = errno; + (void)close(p->fts_symfd); + errno = saved_errno; + SET(FTS_STOP); + return (NULL); + } + (void)close(p->fts_symfd); + } else if (!(p->fts_flags & FTS_DONTCHDIR) && + fts_safe_changedir(sp, p->fts_parent, -1, "..")) { + SET(FTS_STOP); + return (NULL); + } + p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; + return (sp->fts_cur = p); +} + +/* + * Fts_set takes the stream as an argument although it's not used in this + * implementation; it would be necessary if anyone wanted to add global + * semantics to fts using fts_set. An error return is allowed for similar + * reasons. + */ +/* ARGSUSED */ +int +fts_set(sp, p, instr) + FTS *sp; + FTSENT *p; + int instr; +{ + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(p != NULL); + + if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && + instr != FTS_NOINSTR && instr != FTS_SKIP) { + errno = EINVAL; + return (1); + } + p->fts_instr = instr; + return (0); +} + +FTSENT * +fts_children(sp, instr) + FTS *sp; + int instr; +{ + FTSENT *p; + int fd; + + _DIAGASSERT(sp != NULL); + + if (instr && instr != FTS_NAMEONLY) { + errno = EINVAL; + return (NULL); + } + + /* Set current node pointer. */ + p = sp->fts_cur; + + /* + * Errno set to 0 so user can distinguish empty directory from + * an error. + */ + errno = 0; + + /* Fatal errors stop here. */ + if (ISSET(FTS_STOP)) + return (NULL); + + /* Return logical hierarchy of user's arguments. */ + if (p->fts_info == FTS_INIT) + return (p->fts_link); + + /* + * If not a directory being visited in pre-order, stop here. Could + * allow FTS_DNR, assuming the user has fixed the problem, but the + * same effect is available with FTS_AGAIN. + */ + if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) + return (NULL); + + /* Free up any previous child list. */ + if (sp->fts_child) + fts_lfree(sp->fts_child); + + if (instr == FTS_NAMEONLY) { + SET(FTS_NAMEONLY); + instr = BNAMES; + } else + instr = BCHILD; + + /* + * If using chdir on a relative path and called BEFORE fts_read does + * its chdir to the root of a traversal, we can lose -- we need to + * chdir into the subdirectory, and we don't know where the current + * directory is, so we can't get back so that the upcoming chdir by + * fts_read will work. + */ + if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || + ISSET(FTS_NOCHDIR)) + return (sp->fts_child = fts_build(sp, instr)); + + if ((fd = open(".", O_RDONLY, 0)) == -1) + return (sp->fts_child = NULL); + sp->fts_child = fts_build(sp, instr); + if (fchdir(fd)) { + (void)close(fd); + return (NULL); + } + (void)close(fd); + return (sp->fts_child); +} + +/* + * This is the tricky part -- do not casually change *anything* in here. The + * idea is to build the linked list of entries that are used by fts_children + * and fts_read. There are lots of special cases. + * + * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is + * set and it's a physical walk (so that symbolic links can't be directories), + * we can do things quickly. First, if it's a 4.4BSD file system, the type + * of the file is in the directory entry. Otherwise, we assume that the number + * of subdirectories in a node is equal to the number of links to the parent. + * The former skips all stat calls. The latter skips stat calls in any leaf + * directories and for any files after the subdirectories in the directory have + * been found, cutting the stat calls by about 2/3. + */ +static FTSENT * +fts_build(sp, type) + FTS *sp; + int type; +{ + struct dirent *dp; + FTSENT *p, *head; + size_t nitems; + FTSENT *cur, *tail; + DIR *dirp; + int adjust, cderrno, descend, len, level, nlinks, saved_errno, nostat; + size_t maxlen; +#ifdef FTS_WHITEOUT + int oflag; +#endif + char *cp = NULL; /* pacify gcc */ + + _DIAGASSERT(sp != NULL); + + /* Set current node pointer. */ + cur = sp->fts_cur; + + /* + * Open the directory for reading. If this fails, we're done. + * If being called from fts_read, set the fts_info field. + */ +#ifdef FTS_WHITEOUT + if (ISSET(FTS_WHITEOUT)) + oflag = DTF_NODUP|DTF_REWIND; + else + oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; +#else +#define __opendir2(path, flag) opendir(path) +#endif + if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { + if (type == BREAD) { + cur->fts_info = FTS_DNR; + cur->fts_errno = errno; + } + return (NULL); + } + + /* + * Nlinks is the number of possible entries of type directory in the + * directory if we're cheating on stat calls, 0 if we're not doing + * any stat calls at all, -1 if we're doing stats on everything. + */ + if (type == BNAMES) { + nlinks = 0; + nostat = 1; + } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { + nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); + nostat = 1; + } else { + nlinks = -1; + nostat = 0; + } + +#ifdef notdef + (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); + (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", + ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); +#endif + /* + * If we're going to need to stat anything or we want to descend + * and stay in the directory, chdir. If this fails we keep going, + * but set a flag so we don't chdir after the post-order visit. + * We won't be able to stat anything, but we can still return the + * names themselves. Note, that since fts_read won't be able to + * chdir into the directory, it will have to return different path + * names than before, i.e. "a/b" instead of "b". Since the node + * has already been visited in pre-order, have to wait until the + * post-order visit to return the error. There is a special case + * here, if there was nothing to stat then it's not an error to + * not be able to stat. This is all fairly nasty. If a program + * needed sorted entries or stat information, they had better be + * checking FTS_NS on the returned nodes. + */ + cderrno = 0; + if (nlinks || type == BREAD) { + if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { + if (nlinks && type == BREAD) + cur->fts_errno = errno; + cur->fts_flags |= FTS_DONTCHDIR; + descend = 0; + cderrno = errno; + } else + descend = 1; + } else + descend = 0; + + /* + * Figure out the max file name length that can be stored in the + * current path -- the inner loop allocates more path as necessary. + * We really wouldn't have to do the maxlen calculations here, we + * could do them in fts_read before returning the path, but it's a + * lot easier here since the length is part of the dirent structure. + * + * If not changing directories set a pointer so that can just append + * each new name into the path. + */ + len = NAPPEND(cur); + if (ISSET(FTS_NOCHDIR)) { + cp = sp->fts_path + len; + *cp++ = '/'; + } + len++; + maxlen = sp->fts_pathlen - len; + + level = cur->fts_level + 1; + + /* Read the directory, attaching each entry to the `link' pointer. */ + adjust = 0; + for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) { + size_t dlen; + + if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) + continue; + +#if HAVE_STRUCT_DIRENT_D_NAMLEN + dlen = dp->d_namlen; +#else + dlen = strlen(dp->d_name); +#endif + if ((p = fts_alloc(sp, dp->d_name, dlen)) == NULL) + goto mem1; + if (dlen >= maxlen) { /* include space for NUL */ + if (fts_palloc(sp, len + dlen + 1)) { + /* + * No more memory for path or structures. Save + * errno, free up the current structure and the + * structures already allocated. + */ +mem1: saved_errno = errno; + if (p) + free(p); + fts_lfree(head); + (void)closedir(dirp); + errno = saved_errno; + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + return (NULL); + } + adjust = 1; + if (ISSET(FTS_NOCHDIR)) + cp = sp->fts_path + len; + maxlen = sp->fts_pathlen - len; + } + + p->fts_pathlen = len + dlen; + p->fts_parent = sp->fts_cur; + p->fts_level = level; + +#ifdef FTS_WHITEOUT + if (dp->d_type == DT_WHT) + p->fts_flags |= FTS_ISW; +#endif + + if (cderrno) { + if (nlinks) { + p->fts_info = FTS_NS; + p->fts_errno = cderrno; + } else + p->fts_info = FTS_NSOK; + p->fts_accpath = cur->fts_accpath; + } else if (nlinks == 0 +#ifdef DT_DIR + || (nostat && + dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) +#endif + ) { + p->fts_accpath = + ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; + p->fts_info = FTS_NSOK; + } else { + /* Build a file name for fts_stat to stat. */ + if (ISSET(FTS_NOCHDIR)) { + p->fts_accpath = p->fts_path; + memmove(cp, p->fts_name, + (size_t)(p->fts_namelen + 1)); + } else + p->fts_accpath = p->fts_name; + /* Stat it. */ + p->fts_info = fts_stat(sp, p, 0); + + /* Decrement link count if applicable. */ + if (nlinks > 0 && (p->fts_info == FTS_D || + p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) + --nlinks; + } + + /* We walk in directory order so "ls -f" doesn't get upset. */ + p->fts_link = NULL; + if (head == NULL) + head = tail = p; + else { + tail->fts_link = p; + tail = p; + } + ++nitems; + } + (void)closedir(dirp); + + /* + * If had to realloc the path, adjust the addresses for the rest + * of the tree. + */ + if (adjust) + fts_padjust(sp, head); + + /* + * If not changing directories, reset the path back to original + * state. + */ + if (ISSET(FTS_NOCHDIR)) { + if (cp - 1 > sp->fts_path) + --cp; + *cp = '\0'; + } + + /* + * If descended after called from fts_children or after called from + * fts_read and nothing found, get back. At the root level we use + * the saved fd; if one of fts_open()'s arguments is a relative path + * to an empty directory, we wind up here with no other way back. If + * can't get back, we're done. + */ + if (descend && (type == BCHILD || !nitems) && + (cur->fts_level == FTS_ROOTLEVEL ? + FCHDIR(sp, sp->fts_rfd) : + fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + return (NULL); + } + + /* If didn't find anything, return NULL. */ + if (!nitems) { + if (type == BREAD) + cur->fts_info = FTS_DP; + return (NULL); + } + + /* Sort the entries. */ + if (sp->fts_compar && nitems > 1) + head = fts_sort(sp, head, nitems); + return (head); +} + +static u_short +fts_stat(sp, p, follow) + FTS *sp; + FTSENT *p; + int follow; +{ + FTSENT *t; + dev_t dev; + __fts_ino_t ino; + __fts_stat_t *sbp, sb; + int saved_errno; + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(p != NULL); + + /* If user needs stat info, stat buffer already allocated. */ + sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; + +#ifdef FTS_WHITEOUT + /* check for whiteout */ + if (p->fts_flags & FTS_ISW) { + if (sbp != &sb) { + memset(sbp, '\0', sizeof (*sbp)); + sbp->st_mode = S_IFWHT; + } + return (FTS_W); + } +#endif + + /* + * If doing a logical walk, or application requested FTS_FOLLOW, do + * a stat(2). If that fails, check for a non-existent symlink. If + * fail, set the errno from the stat call. + */ + if (ISSET(FTS_LOGICAL) || follow) { + if (stat(p->fts_accpath, sbp)) { + saved_errno = errno; + if (!lstat(p->fts_accpath, sbp)) { + errno = 0; + return (FTS_SLNONE); + } + p->fts_errno = saved_errno; + goto err; + } + } else if (lstat(p->fts_accpath, sbp)) { + p->fts_errno = errno; +err: memset(sbp, 0, sizeof(*sbp)); + return (FTS_NS); + } + + if (S_ISDIR(sbp->st_mode)) { + /* + * Set the device/inode. Used to find cycles and check for + * crossing mount points. Also remember the link count, used + * in fts_build to limit the number of stat calls. It is + * understood that these fields are only referenced if fts_info + * is set to FTS_D. + */ + dev = p->fts_dev = sbp->st_dev; + ino = p->fts_ino = sbp->st_ino; + p->fts_nlink = sbp->st_nlink; + + if (ISDOT(p->fts_name)) + return (FTS_DOT); + + /* + * Cycle detection is done by brute force when the directory + * is first encountered. If the tree gets deep enough or the + * number of symbolic links to directories is high enough, + * something faster might be worthwhile. + */ + for (t = p->fts_parent; + t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) + if (ino == t->fts_ino && dev == t->fts_dev) { + p->fts_cycle = t; + return (FTS_DC); + } + return (FTS_D); + } + if (S_ISLNK(sbp->st_mode)) + return (FTS_SL); + if (S_ISREG(sbp->st_mode)) + return (FTS_F); + return (FTS_DEFAULT); +} + +static FTSENT * +fts_sort(sp, head, nitems) + FTS *sp; + FTSENT *head; + size_t nitems; +{ + FTSENT **ap, *p; + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(head != NULL); + + /* + * Construct an array of pointers to the structures and call qsort(3). + * Reassemble the array in the order returned by qsort. If unable to + * sort for memory reasons, return the directory entries in their + * current order. Allocate enough space for the current needs plus + * 40 so don't realloc one entry at a time. + */ + if (nitems > sp->fts_nitems) { + FTSENT **new; + + new = realloc(sp->fts_array, sizeof(FTSENT *) * (nitems + 40)); + if (new == 0) + return (head); + sp->fts_array = new; + sp->fts_nitems = nitems + 40; + } + for (ap = sp->fts_array, p = head; p; p = p->fts_link) + *ap++ = p; + qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), + (int (*) __P((const void *, const void *)))sp->fts_compar); + for (head = *(ap = sp->fts_array); --nitems; ++ap) + ap[0]->fts_link = ap[1]; + ap[0]->fts_link = NULL; + return (head); +} + +static FTSENT * +fts_alloc(sp, name, namelen) + FTS *sp; + const char *name; + size_t namelen; +{ + FTSENT *p; + size_t len; + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(name != NULL); + +#if defined(ALIGNBYTES) && defined(ALIGN) + /* + * The file name is a variable length array and no stat structure is + * necessary if the user has set the nostat bit. Allocate the FTSENT + * structure, the file name and the stat structure in one chunk, but + * be careful that the stat structure is reasonably aligned. Since the + * fts_name field is declared to be of size 1, the fts_name pointer is + * namelen + 2 before the first possible address of the stat structure. + */ + len = sizeof(FTSENT) + namelen; + if (!ISSET(FTS_NOSTAT)) + len += sizeof(*(p->fts_statp)) + ALIGNBYTES; + if ((p = malloc(len)) == NULL) + return (NULL); + + if (!ISSET(FTS_NOSTAT)) + p->fts_statp = + (__fts_stat_t *)ALIGN((u_long)(p->fts_name + namelen + 2)); +#else + if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL) + return (NULL); + + if (!ISSET(FTS_NOSTAT)) + if ((p->fts_statp = malloc(sizeof(*(p->fts_statp)))) == NULL) { + free(p); + return (NULL); + } +#endif + + /* Copy the name plus the trailing NULL. */ + memmove(p->fts_name, name, namelen + 1); + + p->fts_namelen = namelen; + p->fts_path = sp->fts_path; + p->fts_errno = 0; + p->fts_flags = 0; + p->fts_instr = FTS_NOINSTR; + p->fts_number = 0; + p->fts_pointer = NULL; + return (p); +} + +static void +fts_lfree(head) + FTSENT *head; +{ + FTSENT *p; + + /* XXX: head may be NULL ? */ + + /* Free a linked list of structures. */ + while ((p = head) != NULL) { + head = head->fts_link; + +#if !defined(ALIGNBYTES) || !defined(ALIGN) + if (p->fts_statp) + free(p->fts_statp); +#endif + free(p); + } +} + +static size_t +fts_pow2(x) + size_t x; +{ + + x--; + x |= x>>1; + x |= x>>2; + x |= x>>4; + x |= x>>8; + x |= x>>16; +#if LONG_BIT > 32 + x |= x>>32; +#endif +#if LONG_BIT > 64 + x |= x>>64; +#endif + x++; + return (x); +} + +/* + * Allow essentially unlimited paths; find, rm, ls should all work on any tree. + * Most systems will allow creation of paths much longer than MAXPATHLEN, even + * though the kernel won't resolve them. Round up the new size to a power of 2, + * so we don't realloc the path 2 bytes at a time. + */ +static int +fts_palloc(sp, size) + FTS *sp; + size_t size; +{ + char *new; + + _DIAGASSERT(sp != NULL); + +#if 1 + /* Protect against fts_pathlen overflow. */ + if (size > USHRT_MAX + 1) { + errno = ENOMEM; + return (1); + } +#endif + size = fts_pow2(size); + new = realloc(sp->fts_path, size); + if (new == 0) + return (1); + sp->fts_path = new; + sp->fts_pathlen = size; + return (0); +} + +/* + * When the path is realloc'd, have to fix all of the pointers in structures + * already returned. + */ +static void +fts_padjust(sp, head) + FTS *sp; + FTSENT *head; +{ + FTSENT *p; + char *addr; + + _DIAGASSERT(sp != NULL); + +#define ADJUST(p) do { \ + if ((p)->fts_accpath != (p)->fts_name) \ + (p)->fts_accpath = \ + addr + ((p)->fts_accpath - (p)->fts_path); \ + (p)->fts_path = addr; \ +} while (/*CONSTCOND*/0) + + addr = sp->fts_path; + + /* Adjust the current set of children. */ + for (p = sp->fts_child; p; p = p->fts_link) + ADJUST(p); + + /* Adjust the rest of the tree, including the current level. */ + for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { + ADJUST(p); + p = p->fts_link ? p->fts_link : p->fts_parent; + } +} + +static size_t +fts_maxarglen(argv) + char * const *argv; +{ + size_t len, max; + + _DIAGASSERT(argv != NULL); + + for (max = 0; *argv; ++argv) + if ((len = strlen(*argv)) > max) + max = len; + return (max + 1); +} + +/* + * Change to dir specified by fd or p->fts_accpath without getting + * tricked by someone changing the world out from underneath us. + * Assumes p->fts_dev and p->fts_ino are filled in. + */ +static int +fts_safe_changedir(sp, p, fd, path) + const FTS *sp; + const FTSENT *p; + int fd; + const char *path; +{ + int oldfd = fd, ret = -1; + __fts_stat_t sb; + + if (ISSET(FTS_NOCHDIR)) + return 0; + + if (oldfd < 0 && (fd = open(path, O_RDONLY)) == -1) + return -1; + + if (fstat(fd, &sb) == -1) + goto bail; + + if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) { + errno = ENOENT; + goto bail; + } + + ret = fchdir(fd); + +bail: + if (oldfd < 0) { + int save_errno = errno; + (void)close(fd); + errno = save_errno; + } + return ret; +} diff --git a/lib/libc/gen/__opendir30.c b/lib/libc/gen/__opendir30.c new file mode 100644 index 000000000000..59c98d8e24ca --- /dev/null +++ b/lib/libc/gen/__opendir30.c @@ -0,0 +1,326 @@ +/* $NetBSD: __opendir30.c,v 1.1 2005/08/19 02:04:54 christos Exp $ */ + +/* + * 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. + * 3. 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. + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)opendir.c 8.7 (Berkeley) 12/10/94"; +#else +__RCSID("$NetBSD: __opendir30.c,v 1.1 2005/08/19 02:04:54 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include "namespace.h" +#include "reentrant.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __weak_alias +__weak_alias(opendir,_opendir) +#endif + +#ifdef __LIBC12_SOURCE__ +#define dirent dirent12 +#endif + +/* + * Open a directory. + */ +DIR * +opendir(name) + const char *name; +{ + + _DIAGASSERT(name != NULL); + + return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); +} + +DIR * +__opendir2(name, flags) + const char *name; + int flags; +{ + DIR *dirp = NULL; + int fd; + int serrno; + struct stat sb; + int pagesz; + int incr; + int unionstack, nfsdir; + struct statvfs sfb; + + _DIAGASSERT(name != NULL); + + if ((fd = open(name, O_RDONLY | O_NONBLOCK)) == -1 || + fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + goto error; + if (fstat(fd, &sb) || !S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + goto error; + } + if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) + goto error; + dirp->dd_buf = NULL; + + /* + * If the machine's page size is an exact multiple of DIRBLKSIZ, + * use a buffer that is cluster boundary aligned. + * Hopefully this can be a big win someday by allowing page trades + * to user space to be done by getdirentries() + */ + if (((pagesz = getpagesize()) % DIRBLKSIZ) == 0) + incr = pagesz; + else + incr = DIRBLKSIZ; + + /* + * Determine whether this directory is the top of a union stack. + */ + + if (fstatvfs1(fd, &sfb, ST_NOWAIT) < 0) + goto error; + + if (flags & DTF_NODUP) + unionstack = !(strncmp(sfb.f_fstypename, MOUNT_UNION, + MFSNAMELEN)) || (sfb.f_flag & MNT_UNION); + else + unionstack = 0; + + nfsdir = !(strncmp(sfb.f_fstypename, MOUNT_NFS, MFSNAMELEN)); + + if (unionstack || nfsdir) { + size_t len; + size_t space; + char *buf, *nbuf; + char *ddptr; + char *ddeptr; + int n; + struct dirent **dpv; + + /* + * The strategy here for directories on top of a union stack + * is to read all the directory entries into a buffer, sort + * the buffer, and remove duplicate entries by setting the + * inode number to zero. + * + * For directories on an NFS mounted filesystem, we try + * to get a consistent snapshot by trying until we have + * successfully read all of the directory without errors + * (i.e. 'bad cookie' errors from the server because + * the directory was modified). These errors should not + * happen often, but need to be dealt with. + */ +retry: + len = 0; + space = 0; + buf = 0; + ddptr = 0; + + do { + /* + * Always make at least DIRBLKSIZ bytes + * available to getdirentries + */ + if (space < DIRBLKSIZ) { + space += incr; + len += incr; + nbuf = realloc(buf, len); + if (nbuf == NULL) { + dirp->dd_buf = buf; + goto error; + } + buf = nbuf; + ddptr = buf + (len - space); + } + + dirp->dd_seek = lseek(fd, (off_t)0, SEEK_CUR); + n = getdents(fd, ddptr, space); + /* + * For NFS: EINVAL means a bad cookie error + * from the server. Keep trying to get a + * consistent view, in this case this means + * starting all over again. + */ + if (n == -1 && errno == EINVAL && nfsdir) { + free(buf); + lseek(fd, (off_t)0, SEEK_SET); + goto retry; + } + 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 || + fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { + dirp->dd_buf = buf; + goto error; + } + } + + /* + * 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. + */ + if (!nfsdir) { + for (dpv = 0;;) { + for (n = 0, ddptr = buf; ddptr < ddeptr;) { + struct dirent *dp; + + dp = (struct dirent *)(void *)ddptr; + if ((long)dp & _DIRENT_ALIGN(dp)) + break; + /* + * d_reclen is unsigned, + * so no need to compare <= 0 + */ + if (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, (size_t)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_buf = malloc((size_t)dirp->dd_len); + if (dirp->dd_buf == NULL) + goto error; + dirp->dd_seek = 0; + flags &= ~DTF_REWIND; + } + + dirp->dd_loc = 0; + dirp->dd_fd = fd; + dirp->dd_flags = flags; + + /* + * Set up seek point for rewinddir. + */ +#ifdef _REENTRANT + if (__isthreaded) { + if ((dirp->dd_lock = malloc(sizeof(mutex_t))) == NULL) + goto error; + mutex_init((mutex_t *)dirp->dd_lock, NULL); + } +#endif + dirp->dd_rewind = telldir(dirp); + return (dirp); +error: + serrno = errno; + if (dirp && dirp->dd_buf) + free(dirp->dd_buf); + if (dirp) + free(dirp); + if (fd != -1) + (void)close(fd); + errno = serrno; + return NULL; +} diff --git a/lib/libc/gen/__readdir30.c b/lib/libc/gen/__readdir30.c new file mode 100644 index 000000000000..216721ad8d6d --- /dev/null +++ b/lib/libc/gen/__readdir30.c @@ -0,0 +1,150 @@ +/* $NetBSD: __readdir30.c,v 1.1 2005/08/19 02:04:54 christos Exp $ */ + +/* + * 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. + * 3. 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. + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)readdir.c 8.3 (Berkeley) 9/29/94"; +#else +__RCSID("$NetBSD: __readdir30.c,v 1.1 2005/08/19 02:04:54 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include "namespace.h" +#include "reentrant.h" +#include + +#include +#include +#include +#include + +#ifdef __weak_alias +__weak_alias(readdir,_readdir) +__weak_alias(readdir_r,_readdir_r) +#endif + +#ifdef __LIBC12_SOURCE__ +#define dirent dirent12 +#endif + +/* + * get next entry in a directory. + */ +static struct dirent * +_readdir_unlocked(DIR *dirp) +{ + struct dirent *dp; + + + for (;;) { + if (dirp->dd_loc >= dirp->dd_size) { + if (dirp->dd_flags & __DTF_READALL) + return (NULL); + dirp->dd_loc = 0; + } + if (dirp->dd_loc == 0 && !(dirp->dd_flags & __DTF_READALL)) { + dirp->dd_seek = lseek(dirp->dd_fd, (off_t)0, SEEK_CUR); + dirp->dd_size = getdents(dirp->dd_fd, + dirp->dd_buf, (size_t)dirp->dd_len); + if (dirp->dd_size <= 0) + return (NULL); + } + dp = (struct dirent *) + (void *)(dirp->dd_buf + (size_t)dirp->dd_loc); + if ((intptr_t)dp & _DIRENT_ALIGN(dp))/* bogus pointer check */ + return (NULL); + /* d_reclen is unsigned; no need to compare it <= 0 */ + if (dp->d_reclen > dirp->dd_len + 1 - dirp->dd_loc) + return (NULL); + dirp->dd_loc += dp->d_reclen; + if (dp->d_ino == 0) + continue; + if (dp->d_type == DT_WHT && (dirp->dd_flags & DTF_HIDEW)) + continue; + return (dp); + } +} + +struct dirent * +readdir(dirp) + DIR *dirp; +{ + struct dirent *dp; + +#ifdef _REENTRANT + if (__isthreaded) { + mutex_lock((mutex_t *)dirp->dd_lock); + dp = _readdir_unlocked(dirp); + mutex_unlock((mutex_t *)dirp->dd_lock); + } + else +#endif + dp = _readdir_unlocked(dirp); + return (dp); +} + +int +readdir_r(dirp, entry, result) + DIR *dirp; + struct dirent *entry; + struct dirent **result; +{ + struct dirent *dp; + int saved_errno; + + saved_errno = errno; + errno = 0; +#ifdef _REENTRANT + if (__isthreaded) { + mutex_lock((mutex_t *)dirp->dd_lock); + if ((dp = _readdir_unlocked(dirp)) != NULL) + memcpy(entry, dp, (size_t)_DIRENT_SIZE(dp)); + mutex_unlock((mutex_t *)dirp->dd_lock); + } + else +#endif + if ((dp = _readdir_unlocked(dirp)) != NULL) + memcpy(entry, dp, (size_t)_DIRENT_SIZE(dp)); + + if (errno != 0) { + if (dp == NULL) + return (errno); + } else + errno = saved_errno; + + if (dp != NULL) + *result = entry; + else + *result = NULL; + + return (0); +} diff --git a/lib/libc/gen/__scandir30.c b/lib/libc/gen/__scandir30.c new file mode 100644 index 000000000000..b0159e4b7195 --- /dev/null +++ b/lib/libc/gen/__scandir30.c @@ -0,0 +1,141 @@ +/* $NetBSD: __scandir30.c,v 1.1 2005/08/19 02:04:54 christos Exp $ */ + +/* + * 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. + * 3. 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. + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)scandir.c 8.3 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: __scandir30.c,v 1.1 2005/08/19 02:04:54 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +/* + * Scan the directory dirname calling selectfn to make a list of selected + * directory entries then sort using qsort and compare routine dcomp. + * Returns the number of entries and a pointer to a list of pointers to + * struct dirent (through namelist). Returns -1 if there were any errors. + */ + +#include "namespace.h" +#include +#include + +#include +#include +#include +#include +#include + +#ifdef __weak_alias +__weak_alias(scandir,_scandir) +#endif + +#ifdef __LIBC12_SOURCE__ +#define dirent dirent12 +#endif + +int +scandir(dirname, namelist, selectfn, dcomp) + const char *dirname; + struct dirent ***namelist; + int (*selectfn) __P((const struct dirent *)); + int (*dcomp) __P((const void *, const void *)); +{ + struct dirent *d, *p, **names, **newnames; + size_t nitems, arraysz; + struct stat stb; + DIR *dirp; + + _DIAGASSERT(dirname != NULL); + _DIAGASSERT(namelist != NULL); + + if ((dirp = opendir(dirname)) == NULL) + return (-1); + if (fstat(dirp->dd_fd, &stb) < 0) + goto bad; + + /* + * estimate the array size by taking the size of the directory file + * and dividing it by a multiple of the minimum size entry. + */ + arraysz = (size_t)(stb.st_size / 24); + names = malloc(arraysz * sizeof(struct dirent *)); + if (names == NULL) + goto bad; + + nitems = 0; + while ((d = readdir(dirp)) != NULL) { + if (selectfn != NULL && !(*selectfn)(d)) + continue; /* just selected names */ + + /* + * Check to make sure the array has space left and + * realloc the maximum size. + */ + if (nitems >= arraysz) { + if (fstat(dirp->dd_fd, &stb) < 0) + goto bad2; /* just might have grown */ + arraysz = (size_t)(stb.st_size / 12); + newnames = realloc(names, + arraysz * sizeof(struct dirent *)); + if (newnames == NULL) + goto bad2; + names = newnames; + } + + /* + * Make a minimum size copy of the data + */ + p = (struct dirent *)malloc((size_t)_DIRENT_SIZE(d)); + if (p == NULL) + goto bad2; + p->d_fileno = d->d_fileno; + p->d_reclen = d->d_reclen; + p->d_type = d->d_type; + p->d_namlen = d->d_namlen; + memmove(p->d_name, d->d_name, (size_t)(p->d_namlen + 1)); + names[nitems++] = p; + } + closedir(dirp); + if (nitems && dcomp != NULL) + qsort(names, nitems, sizeof(struct dirent *), dcomp); + *namelist = names; + return (nitems); + +bad2: + while (nitems-- > 0) + free(names[nitems]); + free(names); +bad: + closedir(dirp); + return (-1); +} diff --git a/lib/libc/gen/alphasort.c b/lib/libc/gen/alphasort.c new file mode 100644 index 000000000000..05ac74fd3dba --- /dev/null +++ b/lib/libc/gen/alphasort.c @@ -0,0 +1,63 @@ +/* $NetBSD: alphasort.c,v 1.1 2005/08/19 02:04:54 christos Exp $ */ + +/* + * 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. + * 3. 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. + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)scandir.c 8.3 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: alphasort.c,v 1.1 2005/08/19 02:04:54 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include "namespace.h" + +#include +#include +#include + +#ifdef __weak_alias +__weak_alias(alphasort,_alphasort) +#endif + +/* + * Alphabetic order comparison routine for those who want it. + */ +int +alphasort(const void *d1, const void *d2) +{ + + _DIAGASSERT(d1 != NULL); + _DIAGASSERT(d2 != NULL); + + return (strcmp((*(const struct dirent *const *)d1)->d_name, + (*(const struct dirent *const *)d2)->d_name)); +} diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c index d1ba79f62dab..8beef2296529 100644 --- a/lib/libc/gen/fts.c +++ b/lib/libc/gen/fts.c @@ -1,10 +1,42 @@ -/* $NetBSD: fts.c,v 1.23 2003/12/04 23:39:18 keihan Exp $ */ +/* $NetBSD: fts.c,v 1.24 2005/08/19 02:04:54 christos Exp $ */ /* * Written by Jason R. Thorpe , October 21, 1997. * Public domain. */ +#include "namespace.h" +#include +#include + #define __LIBC12_SOURCE__ -#include "__fts13.c" +#define __fts_stat_t struct stat12 +#define __fts_nlink_t u_int16_t +#define __fts_ino_t u_int32_t + +#ifdef __weak_alias +__weak_alias(fts_children,_fts_children) +__weak_alias(fts_close,_fts_close) +__weak_alias(fts_open,_fts_open) +__weak_alias(fts_read,_fts_read) +__weak_alias(fts_set,_fts_set) +#endif /* __weak_alias */ + +__warn_references(fts_children, + "warning: reference to compatibility fts_children();" + " include for correct reference") +__warn_references(fts_close, + "warning: reference to compatibility fts_close();" + " include for correct reference") +__warn_references(fts_open, + "warning: reference to compatibility fts_open();" + " include for correct reference") +__warn_references(fts_read, + "warning: reference to compatibility fts_read();" + " include for correct reference") +__warn_references(fts_set, + "warning: reference to compatibility fts_set();" + " include for correct reference") + +#include "__fts30.c" diff --git a/lib/libc/gen/opendir.c b/lib/libc/gen/opendir.c index 965155e33307..3c4a809e2a7b 100644 --- a/lib/libc/gen/opendir.c +++ b/lib/libc/gen/opendir.c @@ -1,322 +1,9 @@ -/* $NetBSD: opendir.c,v 1.26 2005/01/19 00:53:33 mycroft Exp $ */ - -/* - * 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. - * 3. 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. - */ - -#include -#if defined(LIBC_SCCS) && !defined(lint) -#if 0 -static char sccsid[] = "@(#)opendir.c 8.7 (Berkeley) 12/10/94"; -#else -__RCSID("$NetBSD: opendir.c,v 1.26 2005/01/19 00:53:33 mycroft Exp $"); -#endif -#endif /* LIBC_SCCS and not lint */ +/* $NetBSD: opendir.c,v 1.27 2005/08/19 02:04:54 christos Exp $ */ #include "namespace.h" -#include "reentrant.h" -#include -#include #include +#include -#include -#include -#include -#include -#include -#include -#include +#define __LIBC12_SOURCE__ -#ifdef __weak_alias -__weak_alias(opendir,_opendir) -#endif - -/* - * Open a directory. - */ -DIR * -opendir(name) - const char *name; -{ - - _DIAGASSERT(name != NULL); - - return (__opendir2(name, DTF_HIDEW|DTF_NODUP)); -} - -DIR * -__opendir2(name, flags) - const char *name; - int flags; -{ - DIR *dirp = NULL; - int fd; - int serrno; - struct stat sb; - int pagesz; - int incr; - int unionstack, nfsdir; - struct statvfs sfb; - - _DIAGASSERT(name != NULL); - - if ((fd = open(name, O_RDONLY | O_NONBLOCK)) == -1 || - fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - goto error; - if (fstat(fd, &sb) || !S_ISDIR(sb.st_mode)) { - errno = ENOTDIR; - goto error; - } - if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) - goto error; - dirp->dd_buf = NULL; - - /* - * If the machine's page size is an exact multiple of DIRBLKSIZ, - * use a buffer that is cluster boundary aligned. - * Hopefully this can be a big win someday by allowing page trades - * to user space to be done by getdirentries() - */ - if (((pagesz = getpagesize()) % DIRBLKSIZ) == 0) - incr = pagesz; - else - incr = DIRBLKSIZ; - - /* - * Determine whether this directory is the top of a union stack. - */ - - if (fstatvfs1(fd, &sfb, ST_NOWAIT) < 0) - goto error; - - if (flags & DTF_NODUP) - unionstack = !(strncmp(sfb.f_fstypename, MOUNT_UNION, - MFSNAMELEN)) || (sfb.f_flag & MNT_UNION); - else - unionstack = 0; - - nfsdir = !(strncmp(sfb.f_fstypename, MOUNT_NFS, MFSNAMELEN)); - - if (unionstack || nfsdir) { - size_t len; - size_t space; - char *buf, *nbuf; - char *ddptr; - char *ddeptr; - int n; - struct dirent **dpv; - - /* - * The strategy here for directories on top of a union stack - * is to read all the directory entries into a buffer, sort - * the buffer, and remove duplicate entries by setting the - * inode number to zero. - * - * For directories on an NFS mounted filesystem, we try - * to get a consistent snapshot by trying until we have - * successfully read all of the directory without errors - * (i.e. 'bad cookie' errors from the server because - * the directory was modified). These errors should not - * happen often, but need to be dealt with. - */ -retry: - len = 0; - space = 0; - buf = 0; - ddptr = 0; - - do { - /* - * Always make at least DIRBLKSIZ bytes - * available to getdirentries - */ - if (space < DIRBLKSIZ) { - space += incr; - len += incr; - nbuf = realloc(buf, len); - if (nbuf == NULL) { - dirp->dd_buf = buf; - goto error; - } - buf = nbuf; - ddptr = buf + (len - space); - } - - dirp->dd_seek = lseek(fd, (off_t)0, SEEK_CUR); - n = getdents(fd, ddptr, space); - /* - * For NFS: EINVAL means a bad cookie error - * from the server. Keep trying to get a - * consistent view, in this case this means - * starting all over again. - */ - if (n == -1 && errno == EINVAL && nfsdir) { - free(buf); - lseek(fd, (off_t)0, SEEK_SET); - goto retry; - } - 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 || - fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { - dirp->dd_buf = buf; - goto error; - } - } - - /* - * 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. - */ - if (!nfsdir) { - for (dpv = 0;;) { - for (n = 0, ddptr = buf; ddptr < ddeptr;) { - struct dirent *dp; - - dp = (struct dirent *)(void *)ddptr; - if ((long)dp & 03) - break; - /* - * d_reclen is unsigned, - * so no need to compare <= 0 - */ - if (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, (size_t)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_buf = malloc((size_t)dirp->dd_len); - if (dirp->dd_buf == NULL) - goto error; - dirp->dd_seek = 0; - flags &= ~DTF_REWIND; - } - - dirp->dd_loc = 0; - dirp->dd_fd = fd; - dirp->dd_flags = flags; - - /* - * Set up seek point for rewinddir. - */ -#ifdef _REENTRANT - if (__isthreaded) { - if ((dirp->dd_lock = malloc(sizeof(mutex_t))) == NULL) - goto error; - mutex_init((mutex_t *)dirp->dd_lock, NULL); - } -#endif - dirp->dd_rewind = telldir(dirp); - return (dirp); -error: - serrno = errno; - if (dirp && dirp->dd_buf) - free(dirp->dd_buf); - if (dirp) - free(dirp); - if (fd != -1) - (void)close(fd); - errno = serrno; - return NULL; -} +#include "__opendir30.c" diff --git a/lib/libc/gen/readdir.c b/lib/libc/gen/readdir.c index 6f7dcf9be286..476da5de24d5 100644 --- a/lib/libc/gen/readdir.c +++ b/lib/libc/gen/readdir.c @@ -1,146 +1,5 @@ -/* $NetBSD: readdir.c,v 1.18 2003/08/07 16:42:55 agc Exp $ */ +/* $NetBSD: readdir.c,v 1.19 2005/08/19 02:04:54 christos Exp $ */ -/* - * 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. - * 3. 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. - */ +#define __LIBC12_SOURCE__ -#include -#if defined(LIBC_SCCS) && !defined(lint) -#if 0 -static char sccsid[] = "@(#)readdir.c 8.3 (Berkeley) 9/29/94"; -#else -__RCSID("$NetBSD: readdir.c,v 1.18 2003/08/07 16:42:55 agc Exp $"); -#endif -#endif /* LIBC_SCCS and not lint */ - -#include "namespace.h" -#include "reentrant.h" -#include - -#include -#include -#include -#include - -#ifdef __weak_alias -__weak_alias(readdir,_readdir) -__weak_alias(readdir_r,_readdir_r) -#endif - -/* - * get next entry in a directory. - */ -static struct dirent * -_readdir_unlocked(DIR *dirp) -{ - struct dirent *dp; - - - for (;;) { - if (dirp->dd_loc >= dirp->dd_size) { - if (dirp->dd_flags & __DTF_READALL) - return (NULL); - dirp->dd_loc = 0; - } - if (dirp->dd_loc == 0 && !(dirp->dd_flags & __DTF_READALL)) { - dirp->dd_seek = lseek(dirp->dd_fd, (off_t)0, SEEK_CUR); - dirp->dd_size = getdents(dirp->dd_fd, - dirp->dd_buf, (size_t)dirp->dd_len); - if (dirp->dd_size <= 0) - return (NULL); - } - dp = (struct dirent *) - (void *)(dirp->dd_buf + (size_t)dirp->dd_loc); - if ((long)dp & 03) /* bogus pointer check */ - return (NULL); - /* d_reclen is unsigned; no need to compare it <= 0 */ - if (dp->d_reclen > dirp->dd_len + 1 - dirp->dd_loc) - return (NULL); - dirp->dd_loc += dp->d_reclen; - if (dp->d_ino == 0) - continue; - if (dp->d_type == DT_WHT && (dirp->dd_flags & DTF_HIDEW)) - continue; - return (dp); - } -} - -struct dirent * -readdir(dirp) - DIR *dirp; -{ - struct dirent *dp; - -#ifdef _REENTRANT - if (__isthreaded) { - mutex_lock((mutex_t *)dirp->dd_lock); - dp = _readdir_unlocked(dirp); - mutex_unlock((mutex_t *)dirp->dd_lock); - } - else -#endif - dp = _readdir_unlocked(dirp); - return (dp); -} - -int -readdir_r(dirp, entry, result) - DIR *dirp; - struct dirent *entry; - struct dirent **result; -{ - struct dirent *dp; - int saved_errno; - - saved_errno = errno; - errno = 0; -#ifdef _REENTRANT - if (__isthreaded) { - mutex_lock((mutex_t *)dirp->dd_lock); - if ((dp = _readdir_unlocked(dirp)) != NULL) - memcpy(entry, dp, DIRENT_SIZE(dp)); - mutex_unlock((mutex_t *)dirp->dd_lock); - } - else -#endif - if ((dp = _readdir_unlocked(dirp)) != NULL) - memcpy(entry, dp, DIRENT_SIZE(dp)); - - if (errno != 0) { - if (dp == NULL) - return (errno); - } else - errno = saved_errno; - - if (dp != NULL) - *result = entry; - else - *result = NULL; - - return (0); -} +#include "__readdir30.c" diff --git a/lib/libc/gen/scandir.c b/lib/libc/gen/scandir.c index a50ebb72fe2b..992815e48590 100644 --- a/lib/libc/gen/scandir.c +++ b/lib/libc/gen/scandir.c @@ -1,165 +1,6 @@ -/* $NetBSD: scandir.c,v 1.23 2005/03/17 10:18:22 kleink Exp $ */ +/* $NetBSD: scandir.c,v 1.24 2005/08/19 02:04:54 christos Exp $ */ -/* - * 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. - * 3. 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. - */ - -#include -#if defined(LIBC_SCCS) && !defined(lint) -#if 0 -static char sccsid[] = "@(#)scandir.c 8.3 (Berkeley) 1/2/94"; -#else -__RCSID("$NetBSD: scandir.c,v 1.23 2005/03/17 10:18:22 kleink Exp $"); -#endif -#endif /* LIBC_SCCS and not lint */ - -/* - * Scan the directory dirname calling selectfn to make a list of selected - * directory entries then sort using qsort and compare routine dcomp. - * Returns the number of entries and a pointer to a list of pointers to - * struct dirent (through namelist). Returns -1 if there were any errors. - */ - -#include "namespace.h" -#include #include +#define __LIBC12_SOURCE__ -#include -#include -#include -#include -#include - -#ifdef __weak_alias -__weak_alias(scandir,_scandir) -__weak_alias(alphasort,_alphasort) -#endif - -/* - * The DIRSIZ macro is the minimum record length which will hold the directory - * entry. This requires the amount of space in struct dirent without the - * d_name field, plus enough space for the name and a terminating nul byte - * (dp->d_namlen + 1), rounded up to a 4 byte boundary. - */ -#undef DIRSIZ -#define DIRSIZ(dp) \ - ((sizeof(struct dirent) - sizeof(dp)->d_name) + \ - (((dp)->d_namlen + 1 + 3) &~ 3)) - -int -scandir(dirname, namelist, selectfn, dcomp) - const char *dirname; - struct dirent ***namelist; - int (*selectfn) __P((const struct dirent *)); - int (*dcomp) __P((const void *, const void *)); -{ - struct dirent *d, *p, **names, **newnames; - size_t nitems, arraysz; - struct stat stb; - DIR *dirp; - - _DIAGASSERT(dirname != NULL); - _DIAGASSERT(namelist != NULL); - - if ((dirp = opendir(dirname)) == NULL) - return (-1); - if (fstat(dirp->dd_fd, &stb) < 0) - goto bad; - - /* - * estimate the array size by taking the size of the directory file - * and dividing it by a multiple of the minimum size entry. - */ - arraysz = (size_t)(stb.st_size / 24); - names = malloc(arraysz * sizeof(struct dirent *)); - if (names == NULL) - goto bad; - - nitems = 0; - while ((d = readdir(dirp)) != NULL) { - if (selectfn != NULL && !(*selectfn)(d)) - continue; /* just selected names */ - - /* - * Check to make sure the array has space left and - * realloc the maximum size. - */ - if (nitems >= arraysz) { - if (fstat(dirp->dd_fd, &stb) < 0) - goto bad2; /* just might have grown */ - arraysz = (size_t)(stb.st_size / 12); - newnames = realloc(names, - arraysz * sizeof(struct dirent *)); - if (newnames == NULL) - goto bad2; - names = newnames; - } - - /* - * Make a minimum size copy of the data - */ - p = (struct dirent *)malloc(DIRSIZ(d)); - if (p == NULL) - goto bad2; - p->d_fileno = d->d_fileno; - p->d_reclen = d->d_reclen; - p->d_type = d->d_type; - p->d_namlen = d->d_namlen; - memmove(p->d_name, d->d_name, (size_t)(p->d_namlen + 1)); - names[nitems++] = p; - } - closedir(dirp); - if (nitems && dcomp != NULL) - qsort(names, nitems, sizeof(struct dirent *), dcomp); - *namelist = names; - return (nitems); - -bad2: - while (nitems-- > 0) - free(names[nitems]); - free(names); -bad: - closedir(dirp); - return (-1); -} - -/* - * Alphabetic order comparison routine for those who want it. - */ -int -alphasort(d1, d2) - const void *d1; - const void *d2; -{ - - _DIAGASSERT(d1 != NULL); - _DIAGASSERT(d2 != NULL); - - return (strcmp((*(const struct dirent *const *)d1)->d_name, - (*(const struct dirent *const *)d2)->d_name)); -} +#include "__scandir30.c" diff --git a/lib/libc/shlib_version b/lib/libc/shlib_version index f4a3b7b67dfc..653da090fc18 100644 --- a/lib/libc/shlib_version +++ b/lib/libc/shlib_version @@ -1,4 +1,4 @@ -# $NetBSD: shlib_version,v 1.167 2005/08/07 20:32:58 veego Exp $ +# $NetBSD: shlib_version,v 1.168 2005/08/19 02:04:54 christos Exp $ # Remember to update distrib/sets/lists/base/shl.* when changing # # things we wish to do on next major version bump: @@ -17,4 +17,4 @@ # - libc/net/getnet{ent,namadr}.c, netdb.h: remove __n_pad0 # major=12 -minor=132 +minor=133 diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index c3bf7fe384f5..0d02b50115b3 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -1,4 +1,4 @@ -# $NetBSD: Makefile.inc,v 1.158 2005/07/16 17:12:12 christos Exp $ +# $NetBSD: Makefile.inc,v 1.159 2005/08/19 02:04:54 christos Exp $ # @(#)Makefile.inc 8.3 (Berkeley) 10/24/94 # sys sources @@ -28,9 +28,10 @@ SRCS+= sigtimedwait.c sigwait.c sigwaitinfo.c statvfs.c # glue to provide compatibility between GCC 1.X and 2.X and for compat # with old syscall interfaces. -SRCS+= adjtime.c clock_settime.c ftruncate.c getdirentries.c lseek.c mmap.c \ - msync.c ntp_adjtime.c pread.c preadv.c pwrite.c pwritev.c semctl.c \ - settimeofday.c sigaltstack.c stat.c statfs.c swapon.c truncate.c +SRCS+= adjtime.c clock_settime.c ftruncate.c getdirentries.c getdents.c \ + lseek.c mmap.c msync.c ntp_adjtime.c pread.c preadv.c pwrite.c \ + pwritev.c semctl.c settimeofday.c sigaltstack.c stat.c __stat13.c \ + statfs.c swapon.c truncate.c # glue to syscalls that may pass structs or unions, which cannot be # handled portably and easily by the syscall interface. The glue @@ -54,8 +55,8 @@ ASM= access.S acct.S \ extattr_set_link.S \ fchdir.S fchflags.S fchmod.S fchown.S fchroot.S \ fhopen.S fhstat.S fhstatvfs1.S fktrace.S \ - flock.S fpathconf.S __fstat13.S fstatvfs1.S futimes.S \ - __getcwd.S getdents.S getfh.S getvfsstat.S getgroups.S getitimer.S \ + flock.S fpathconf.S __fstat30.S fstatvfs1.S futimes.S \ + __getcwd.S __getdents30.S getfh.S getvfsstat.S getgroups.S getitimer.S \ __getlogin.S getpeername.S getpgid.S getpgrp.S \ getpriority.S getrlimit.S getrusage.S getsid.S getsockname.S \ getsockopt.S gettimeofday.S \ @@ -65,7 +66,7 @@ ASM= access.S acct.S \ _ksem_post.S _ksem_trywait.S _ksem_unlink.S _ksem_wait.S \ _ksem_open.S \ lchflags.S lchmod.S lchown.S lfs_bmapv.S lfs_markv.S lfs_segclean.S \ - lfs_segwait.S link.S listen.S __lstat13.S lutimes.S \ + lfs_segwait.S link.S listen.S __lstat30.S lutimes.S \ _lwp_create.S _lwp_exit.S _lwp_self.S _lwp_wait.S \ _lwp_suspend.S _lwp_continue.S _lwp_wakeup.S \ _lwp_getprivate.S _lwp_setprivate.S \ @@ -85,7 +86,7 @@ ASM= access.S acct.S \ setregid.S setreuid.S setrlimit.S setsid.S setsockopt.S \ setuid.S __shmctl13.S shmdt.S shmget.S shutdown.S \ __sigaltstack14.S __sigpending14.S __sigaction_sigtramp.S \ - __sigtimedwait.S socket.S socketpair.S __stat13.S statvfs1.S \ + __sigtimedwait.S socket.S socketpair.S __stat30.S statvfs1.S \ swapctl.S symlink.S __sysctl.S \ timer_create.S timer_delete.S timer_gettime.S timer_getoverrun.S \ timer_settime.S \ diff --git a/lib/libc/sys/__stat13.c b/lib/libc/sys/__stat13.c new file mode 100644 index 000000000000..4c54cfcc5fd3 --- /dev/null +++ b/lib/libc/sys/__stat13.c @@ -0,0 +1,115 @@ +/* $NetBSD: __stat13.c,v 1.1 2005/08/19 02:04:54 christos Exp $ */ + +/* + * Copyright (c) 1997 Frank van der Linden + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by Frank van der Linden + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: __stat13.c,v 1.1 2005/08/19 02:04:54 christos Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#define __LIBC12_SOURCE__ + +#include +#include + +__warn_references(__stat13, + "warning: reference to compatibility __stat13(); include to generate correct reference") + +__warn_references(__fstat13, + "warning: reference to compatibility __fstat13(); include to generate correct reference") + +__warn_references(__lstat13, + "warning: reference to compatibility __lstat13(); include to generate correct reference") + +/* + * Convert from a new to an old stat structure. + */ + +static void cvtstat(struct stat13 *, const struct stat *); + +static void +cvtstat(struct stat13 *ost, const struct stat *st) +{ + + ost->st_dev = st->st_dev; + ost->st_ino = (uint32_t)st->st_ino; + ost->st_mode = st->st_mode; + ost->st_nlink = st->st_nlink; + ost->st_uid = st->st_uid; + ost->st_gid = st->st_gid; + ost->st_rdev = st->st_rdev; + ost->st_atimespec = st->st_atimespec; + ost->st_mtimespec = st->st_mtimespec; + ost->st_ctimespec = st->st_ctimespec; + ost->st_birthtimespec = st->st_birthtimespec; + ost->st_size = st->st_size; + ost->st_blocks = st->st_blocks; + ost->st_blksize = st->st_blksize; + ost->st_flags = st->st_flags; + ost->st_gen = st->st_gen; +} + +int +__stat13(const char *file, struct stat13 *ost) +{ + struct stat nst; + int ret; + + if ((ret = __stat30(file, &nst)) == -1) + return ret; + cvtstat(ost, &nst); + return ret; +} + +int +__fstat13(int f, struct stat13 *ost) +{ + struct stat nst; + int ret; + + if ((ret = __fstat30(f, &nst)) == -1) + return ret; + cvtstat(ost, &nst); + return ret; +} + +int +__lstat13(const char *file, struct stat13 *ost) +{ + struct stat nst; + int ret; + + if ((ret = __lstat30(file, &nst)) == -1) + return ret; + cvtstat(ost, &nst); + return ret; +} diff --git a/lib/libc/sys/getdents.2 b/lib/libc/sys/getdents.2 index 231edbb52db4..771fde7b2c24 100644 --- a/lib/libc/sys/getdents.2 +++ b/lib/libc/sys/getdents.2 @@ -1,4 +1,4 @@ -.\" $NetBSD: getdents.2,v 1.15 2004/05/13 10:20:58 wiz Exp $ +.\" $NetBSD: getdents.2,v 1.16 2005/08/19 02:04:54 christos Exp $ .\" .\" Copyright (c) 1989, 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -29,7 +29,7 @@ .\" .\" @(#)getdirentries.2 8.1 (Berkeley) 6/9/93 .\" -.Dd June 9, 1993 +.Dd August 15, 2005 .Dt GETDENTS 2 .Os .Sh NAME @@ -65,7 +65,7 @@ The data in the buffer is a series of .Em dirent structures each containing the following entries: .Bd -literal -offset indent -u_int32_t d_fileno; +ino_t d_fileno; u_int16_t d_reclen; u_int8_t d_type; u_int8_t d_namlen; diff --git a/lib/libc/sys/getdents.c b/lib/libc/sys/getdents.c new file mode 100644 index 000000000000..9ce455d6108e --- /dev/null +++ b/lib/libc/sys/getdents.c @@ -0,0 +1,87 @@ +/* $NetBSD: getdents.c,v 1.1 2005/08/19 02:04:54 christos Exp $ */ + +/*- + * Copyright (c) 2005 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: getdents.c,v 1.1 2005/08/19 02:04:54 christos Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#define __LIBC12_SOURCE__ + +#include "namespace.h" +#include +#include +#include + +/* + * libc12 compatible getdents routine. + */ +int +getdents(int fd, char *buf, size_t nbytes) +{ + struct dirent *ndp, *nndp, *endp; + struct dirent12 *odp; + int rv; + + if ((rv = __getdents30(fd, buf, nbytes)) == -1) + return rv; + + odp = (struct dirent12 *)(void *)buf; + ndp = (struct dirent *)(void *)buf; + endp = (struct dirent *)(void *)&buf[rv]; + + /* + * In-place conversion. This works because odp + * is smaller than ndp, but it has to be done + * in the right sequence. + */ + for (; ndp < endp; ndp = nndp) { + nndp = _DIRENT_NEXT(ndp); + odp->d_ino = (u_int32_t)ndp->d_ino; + if (ndp->d_namlen >= sizeof(odp->d_name)) + odp->d_namlen = sizeof(odp->d_name) - 1; + else + odp->d_namlen = (u_int8_t)ndp->d_namlen; + odp->d_type = ndp->d_type; + (void)memcpy(odp->d_name, ndp->d_name, (size_t)odp->d_namlen); + odp->d_name[odp->d_namlen] = '\0'; + odp->d_reclen = _DIRENT_SIZE(odp); + odp = _DIRENT_NEXT(odp); + } + return ((char *)(void *)odp) - buf; +} diff --git a/lib/libc/sys/getdirentries.c b/lib/libc/sys/getdirentries.c index 0e76089f2b74..d7a13ca491c0 100644 --- a/lib/libc/sys/getdirentries.c +++ b/lib/libc/sys/getdirentries.c @@ -1,4 +1,4 @@ -/* $NetBSD: getdirentries.c,v 1.4 2005/06/12 05:21:28 lukem Exp $ */ +/* $NetBSD: getdirentries.c,v 1.5 2005/08/19 02:04:54 christos Exp $ */ /* * Copyright (c) 1997 Frank van der Linden @@ -34,9 +34,10 @@ #include #if defined(LIBC_SCCS) && !defined(lint) -__RCSID("$NetBSD: getdirentries.c,v 1.4 2005/06/12 05:21:28 lukem Exp $"); +__RCSID("$NetBSD: getdirentries.c,v 1.5 2005/08/19 02:04:54 christos Exp $"); #endif /* LIBC_SCCS and not lint */ +#define __LIBC12_SOURCE__ #include "namespace.h" #include #include diff --git a/lib/libc/sys/stat.c b/lib/libc/sys/stat.c index 4000441c278a..f11efb5f9db5 100644 --- a/lib/libc/sys/stat.c +++ b/lib/libc/sys/stat.c @@ -1,4 +1,4 @@ -/* $NetBSD: stat.c,v 1.4 2005/06/12 05:21:28 lukem Exp $ */ +/* $NetBSD: stat.c,v 1.5 2005/08/19 02:04:54 christos Exp $ */ /* * Copyright (c) 1997 Frank van der Linden @@ -33,7 +33,7 @@ #include #if defined(LIBC_SCCS) && !defined(lint) -__RCSID("$NetBSD: stat.c,v 1.4 2005/06/12 05:21:28 lukem Exp $"); +__RCSID("$NetBSD: stat.c,v 1.5 2005/08/19 02:04:54 christos Exp $"); #endif /* LIBC_SCCS and not lint */ #define __LIBC12_SOURCE__ @@ -63,7 +63,7 @@ cvtstat(st, ost) { ost->st_dev = st->st_dev; - ost->st_ino = st->st_ino; + ost->st_ino = (u_int32_t)st->st_ino; ost->st_mode = st->st_mode; if (st->st_nlink >= (1 << 15)) ost->st_nlink = (1 << 15) - 1; @@ -90,7 +90,7 @@ stat(file, ost) struct stat nst; int ret; - if ((ret = __stat13(file, &nst)) < 0) + if ((ret = __stat30(file, &nst)) < 0) return ret; cvtstat(&nst, ost); return ret; @@ -104,7 +104,7 @@ fstat(f, ost) struct stat nst; int ret; - if ((ret = __fstat13(f, &nst)) < 0) + if ((ret = __fstat30(f, &nst)) < 0) return ret; cvtstat(&nst, ost); return ret; @@ -118,7 +118,7 @@ lstat(file, ost) struct stat nst; int ret; - if ((ret = __lstat13(file, &nst)) < 0) + if ((ret = __lstat30(file, &nst)) < 0) return ret; cvtstat(&nst, ost); return ret;