1 1.30 christos /* $NetBSD: scandir.c,v 1.30 2022/03/12 17:31:39 christos Exp $ */ 2 1.6 cgd 3 1.25 christos /* 4 1.25 christos * Copyright (c) 1983, 1993 5 1.25 christos * The Regents of the University of California. All rights reserved. 6 1.25 christos * 7 1.25 christos * Redistribution and use in source and binary forms, with or without 8 1.25 christos * modification, are permitted provided that the following conditions 9 1.25 christos * are met: 10 1.25 christos * 1. Redistributions of source code must retain the above copyright 11 1.25 christos * notice, this list of conditions and the following disclaimer. 12 1.25 christos * 2. Redistributions in binary form must reproduce the above copyright 13 1.25 christos * notice, this list of conditions and the following disclaimer in the 14 1.25 christos * documentation and/or other materials provided with the distribution. 15 1.25 christos * 3. Neither the name of the University nor the names of its contributors 16 1.25 christos * may be used to endorse or promote products derived from this software 17 1.25 christos * without specific prior written permission. 18 1.25 christos * 19 1.25 christos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.25 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.25 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.25 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.25 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.25 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.25 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.25 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.25 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.25 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.25 christos * SUCH DAMAGE. 30 1.25 christos */ 31 1.25 christos 32 1.25 christos #include <sys/cdefs.h> 33 1.25 christos #if defined(LIBC_SCCS) && !defined(lint) 34 1.25 christos #if 0 35 1.25 christos static char sccsid[] = "@(#)scandir.c 8.3 (Berkeley) 1/2/94"; 36 1.25 christos #else 37 1.30 christos __RCSID("$NetBSD: scandir.c,v 1.30 2022/03/12 17:31:39 christos Exp $"); 38 1.25 christos #endif 39 1.25 christos #endif /* LIBC_SCCS and not lint */ 40 1.25 christos 41 1.25 christos /* 42 1.25 christos * Scan the directory dirname calling selectfn to make a list of selected 43 1.25 christos * directory entries then sort using qsort and compare routine dcomp. 44 1.25 christos * Returns the number of entries and a pointer to a list of pointers to 45 1.25 christos * struct dirent (through namelist). Returns -1 if there were any errors. 46 1.25 christos */ 47 1.25 christos 48 1.25 christos #include "namespace.h" 49 1.25 christos #include <sys/types.h> 50 1.1 cgd #include <sys/stat.h> 51 1.15 lukem 52 1.25 christos #include <assert.h> 53 1.25 christos #include <errno.h> 54 1.25 christos #include <dirent.h> 55 1.25 christos #include <stdlib.h> 56 1.25 christos #include <string.h> 57 1.25 christos 58 1.26 christos /* 59 1.26 christos * Compute an estimate of the number of entries in a directory based on 60 1.26 christos * the file size. Returns the estimated number of entries or 0 on failure. 61 1.26 christos */ 62 1.26 christos static size_t 63 1.26 christos dirsize(int fd, size_t olen) 64 1.26 christos { 65 1.26 christos struct stat stb; 66 1.26 christos size_t nlen; 67 1.26 christos 68 1.26 christos if (fstat(fd, &stb) == -1) 69 1.26 christos return 0; 70 1.26 christos /* 71 1.26 christos * Estimate the array size by taking the size of the directory file 72 1.26 christos * and dividing it by a multiple of the minimum size entry. 73 1.26 christos */ 74 1.26 christos nlen = (size_t)(stb.st_size / _DIRENT_MINSIZE((struct dirent *)0)); 75 1.26 christos /* 76 1.26 christos * If the size turns up 0, switch to an alternate strategy and use the 77 1.26 christos * file size as the number of entries like ZFS returns. If that turns 78 1.26 christos * out to be 0 too return a minimum of 10 entries, plus the old length. 79 1.26 christos */ 80 1.26 christos if (nlen == 0) 81 1.26 christos nlen = (size_t)(stb.st_size ? stb.st_size : 10); 82 1.26 christos return olen + nlen; 83 1.26 christos } 84 1.26 christos 85 1.28 mrg #ifndef COMPARARG 86 1.28 mrg #define COMPARARG struct dirent ** 87 1.28 mrg #endif 88 1.28 mrg 89 1.25 christos int 90 1.26 christos scandir(const char *dirname, struct dirent ***namelist, 91 1.26 christos int (*selectfn)(const struct dirent *), 92 1.28 mrg int (*dcomp)(const COMPARARG, const COMPARARG)) 93 1.25 christos { 94 1.29 nia struct dirent *d, *p, **names; 95 1.25 christos size_t nitems, arraysz; 96 1.25 christos DIR *dirp; 97 1.30 christos int serrno; 98 1.25 christos 99 1.25 christos _DIAGASSERT(dirname != NULL); 100 1.25 christos _DIAGASSERT(namelist != NULL); 101 1.25 christos 102 1.25 christos if ((dirp = opendir(dirname)) == NULL) 103 1.26 christos return -1; 104 1.26 christos 105 1.26 christos if ((arraysz = dirsize(dirp->dd_fd, 0)) == 0) 106 1.25 christos goto bad; 107 1.25 christos 108 1.29 nia names = NULL; 109 1.30 christos errno = reallocarr(&names, arraysz, sizeof(*names)); 110 1.30 christos if (errno) 111 1.25 christos goto bad; 112 1.25 christos 113 1.25 christos nitems = 0; 114 1.25 christos while ((d = readdir(dirp)) != NULL) { 115 1.25 christos if (selectfn != NULL && !(*selectfn)(d)) 116 1.25 christos continue; /* just selected names */ 117 1.25 christos 118 1.25 christos /* 119 1.25 christos * Check to make sure the array has space left and 120 1.25 christos * realloc the maximum size. 121 1.25 christos */ 122 1.25 christos if (nitems >= arraysz) { 123 1.26 christos if ((arraysz = dirsize(dirp->dd_fd, arraysz)) == 0) 124 1.26 christos goto bad2; 125 1.30 christos errno = reallocarr(&names, arraysz, sizeof(*names)); 126 1.30 christos if (errno) 127 1.25 christos goto bad2; 128 1.25 christos } 129 1.25 christos 130 1.25 christos /* 131 1.25 christos * Make a minimum size copy of the data 132 1.25 christos */ 133 1.26 christos p = malloc((size_t)_DIRENT_SIZE(d)); 134 1.25 christos if (p == NULL) 135 1.25 christos goto bad2; 136 1.25 christos p->d_fileno = d->d_fileno; 137 1.25 christos p->d_reclen = d->d_reclen; 138 1.25 christos p->d_type = d->d_type; 139 1.25 christos p->d_namlen = d->d_namlen; 140 1.26 christos (void)memmove(p->d_name, d->d_name, (size_t)(p->d_namlen + 1)); 141 1.25 christos names[nitems++] = p; 142 1.25 christos } 143 1.26 christos (void)closedir(dirp); 144 1.25 christos if (nitems && dcomp != NULL) 145 1.28 mrg qsort(names, nitems, sizeof(*names), 146 1.28 mrg (int (*)(const void *, const void *))dcomp); 147 1.25 christos *namelist = names; 148 1.27 christos _DIAGASSERT(__type_fit(int, nitems)); 149 1.27 christos return (int)nitems; 150 1.25 christos 151 1.25 christos bad2: 152 1.30 christos serrno = errno; 153 1.25 christos while (nitems-- > 0) 154 1.25 christos free(names[nitems]); 155 1.25 christos free(names); 156 1.30 christos errno = serrno; 157 1.25 christos bad: 158 1.30 christos serrno = errno; 159 1.26 christos (void)closedir(dirp); 160 1.30 christos errno = serrno; 161 1.26 christos return -1; 162 1.25 christos } 163