1 1.5 kre /* $NetBSD: initdir.c,v 1.5 2021/07/11 16:30:41 kre Exp $ */ 2 1.1 yamt 3 1.1 yamt /* 4 1.1 yamt * Copyright (c) 1983, 1993 5 1.1 yamt * The Regents of the University of California. All rights reserved. 6 1.1 yamt * 7 1.1 yamt * Redistribution and use in source and binary forms, with or without 8 1.1 yamt * modification, are permitted provided that the following conditions 9 1.1 yamt * are met: 10 1.1 yamt * 1. Redistributions of source code must retain the above copyright 11 1.1 yamt * notice, this list of conditions and the following disclaimer. 12 1.1 yamt * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 yamt * notice, this list of conditions and the following disclaimer in the 14 1.1 yamt * documentation and/or other materials provided with the distribution. 15 1.1 yamt * 3. Neither the name of the University nor the names of its contributors 16 1.1 yamt * may be used to endorse or promote products derived from this software 17 1.1 yamt * without specific prior written permission. 18 1.1 yamt * 19 1.1 yamt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 yamt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 yamt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 yamt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 yamt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 yamt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 yamt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 yamt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 yamt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 yamt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 yamt * SUCH DAMAGE. 30 1.1 yamt */ 31 1.1 yamt 32 1.1 yamt #include <sys/cdefs.h> 33 1.1 yamt #if defined(LIBC_SCCS) && !defined(lint) 34 1.5 kre __RCSID("$NetBSD: initdir.c,v 1.5 2021/07/11 16:30:41 kre Exp $"); 35 1.1 yamt #endif /* LIBC_SCCS and not lint */ 36 1.1 yamt 37 1.1 yamt #include "namespace.h" 38 1.1 yamt #include "reentrant.h" 39 1.1 yamt #include "extern.h" 40 1.1 yamt 41 1.1 yamt #include <sys/param.h> 42 1.1 yamt 43 1.1 yamt #include <assert.h> 44 1.1 yamt #include <dirent.h> 45 1.1 yamt #include <errno.h> 46 1.1 yamt #include <fcntl.h> 47 1.1 yamt #include <stdlib.h> 48 1.1 yamt #include <string.h> 49 1.1 yamt #include <unistd.h> 50 1.1 yamt 51 1.1 yamt #include "dirent_private.h" 52 1.1 yamt 53 1.1 yamt #define MAXITERATIONS 100 54 1.1 yamt 55 1.1 yamt int 56 1.1 yamt _initdir(DIR *dirp, int fd, const char *name) 57 1.1 yamt { 58 1.1 yamt int flags = dirp->dd_flags; 59 1.1 yamt int pagesz; 60 1.1 yamt int incr; 61 1.1 yamt 62 1.1 yamt /* 63 1.1 yamt * If the machine's page size is an exact multiple of DIRBLKSIZ, 64 1.1 yamt * use a buffer that is cluster boundary aligned. 65 1.1 yamt * Hopefully this can be a big win someday by allowing page trades 66 1.1 yamt * to user space to be done by getdents() 67 1.1 yamt */ 68 1.1 yamt if (((pagesz = getpagesize()) % DIRBLKSIZ) == 0) 69 1.1 yamt incr = pagesz; 70 1.1 yamt else 71 1.1 yamt incr = DIRBLKSIZ; 72 1.1 yamt 73 1.1 yamt if ((flags & DTF_REWIND) && name == NULL) { 74 1.1 yamt return EINVAL; 75 1.1 yamt } 76 1.1 yamt if ((flags & __DTF_READALL) != 0) { 77 1.1 yamt size_t len; 78 1.1 yamt size_t space; 79 1.1 yamt char *buf, *nbuf; 80 1.1 yamt char *ddptr; 81 1.1 yamt char *ddeptr; 82 1.1 yamt int n; 83 1.1 yamt struct dirent **dpv; 84 1.1 yamt int i; 85 1.1 yamt 86 1.1 yamt /* 87 1.1 yamt * The strategy here for directories on top of a union stack 88 1.1 yamt * is to read all the directory entries into a buffer, sort 89 1.1 yamt * the buffer, and remove duplicate entries by setting the 90 1.1 yamt * inode number to zero. 91 1.1 yamt * 92 1.1 yamt * For directories on an NFS mounted filesystem, we try 93 1.1 yamt * to get a consistent snapshot by trying until we have 94 1.1 yamt * successfully read all of the directory without errors 95 1.1 yamt * (i.e. 'bad cookie' errors from the server because 96 1.1 yamt * the directory was modified). These errors should not 97 1.1 yamt * happen often, but need to be dealt with. 98 1.1 yamt */ 99 1.1 yamt i = 0; 100 1.1 yamt retry: 101 1.1 yamt len = 0; 102 1.1 yamt space = 0; 103 1.1 yamt buf = 0; 104 1.1 yamt ddptr = 0; 105 1.1 yamt 106 1.1 yamt do { 107 1.1 yamt /* 108 1.1 yamt * Always make at least DIRBLKSIZ bytes 109 1.1 yamt * available to getdents 110 1.1 yamt */ 111 1.1 yamt if (space < DIRBLKSIZ) { 112 1.1 yamt space += incr; 113 1.1 yamt len += incr; 114 1.1 yamt nbuf = realloc(buf, len); 115 1.1 yamt if (nbuf == NULL) { 116 1.1 yamt dirp->dd_buf = buf; 117 1.1 yamt return errno; 118 1.1 yamt } 119 1.1 yamt buf = nbuf; 120 1.1 yamt ddptr = buf + (len - space); 121 1.1 yamt } 122 1.1 yamt 123 1.1 yamt dirp->dd_seek = lseek(fd, (off_t)0, SEEK_CUR); 124 1.1 yamt n = getdents(fd, ddptr, space); 125 1.1 yamt /* 126 1.1 yamt * For NFS: EINVAL means a bad cookie error 127 1.1 yamt * from the server. Keep trying to get a 128 1.1 yamt * consistent view, in this case this means 129 1.1 yamt * starting all over again. 130 1.1 yamt */ 131 1.1 yamt if (n == -1 && errno == EINVAL && 132 1.1 yamt (flags & __DTF_RETRY_ON_BADCOOKIE) != 0) { 133 1.1 yamt free(buf); 134 1.1 yamt lseek(fd, (off_t)0, SEEK_SET); 135 1.1 yamt if (++i > MAXITERATIONS) 136 1.1 yamt return EINVAL; 137 1.1 yamt goto retry; 138 1.1 yamt } 139 1.1 yamt if (n > 0) { 140 1.1 yamt ddptr += n; 141 1.1 yamt space -= n; 142 1.1 yamt } 143 1.1 yamt } while (n > 0); 144 1.1 yamt 145 1.1 yamt ddeptr = ddptr; 146 1.1 yamt 147 1.1 yamt /* 148 1.1 yamt * Re-open the directory. 149 1.1 yamt * This has the effect of rewinding back to the 150 1.1 yamt * top of the union stack and is needed by 151 1.1 yamt * programs which plan to fchdir to a descriptor 152 1.1 yamt * which has also been read -- see fts.c. 153 1.1 yamt */ 154 1.1 yamt if (flags & DTF_REWIND) { 155 1.1 yamt (void) close(fd); 156 1.2 christos if ((fd = open(name, O_RDONLY | O_CLOEXEC)) == -1) { 157 1.1 yamt dirp->dd_buf = buf; 158 1.1 yamt return errno; 159 1.1 yamt } 160 1.1 yamt } 161 1.1 yamt 162 1.1 yamt /* 163 1.1 yamt * There is now a buffer full of (possibly) duplicate 164 1.1 yamt * names. 165 1.1 yamt */ 166 1.1 yamt dirp->dd_buf = buf; 167 1.1 yamt 168 1.1 yamt /* 169 1.1 yamt * Go round this loop twice... 170 1.1 yamt * 171 1.1 yamt * Scan through the buffer, counting entries. 172 1.1 yamt * On the second pass, save pointers to each one. 173 1.1 yamt * Then sort the pointers and remove duplicate names. 174 1.1 yamt */ 175 1.1 yamt if ((flags & DTF_NODUP) != 0) { 176 1.1 yamt for (dpv = 0;;) { 177 1.1 yamt for (n = 0, ddptr = buf; ddptr < ddeptr;) { 178 1.1 yamt struct dirent *dp; 179 1.1 yamt 180 1.1 yamt dp = (struct dirent *)(void *)ddptr; 181 1.1 yamt if ((long)dp & _DIRENT_ALIGN(dp)) 182 1.1 yamt break; 183 1.1 yamt /* 184 1.1 yamt * d_reclen is unsigned, 185 1.1 yamt * so no need to compare <= 0 186 1.1 yamt */ 187 1.1 yamt if (dp->d_reclen > (ddeptr + 1 - ddptr)) 188 1.1 yamt break; 189 1.1 yamt ddptr += dp->d_reclen; 190 1.1 yamt if (dp->d_fileno) { 191 1.1 yamt if (dpv) 192 1.1 yamt dpv[n] = dp; 193 1.1 yamt n++; 194 1.1 yamt } 195 1.1 yamt } 196 1.1 yamt 197 1.1 yamt if (dpv) { 198 1.1 yamt struct dirent *xp; 199 1.1 yamt 200 1.1 yamt /* 201 1.1 yamt * This sort must be stable. 202 1.1 yamt */ 203 1.1 yamt mergesort(dpv, (size_t)n, sizeof(*dpv), 204 1.4 mrg (int (*)(const void *, 205 1.4 mrg const void *))alphasort); 206 1.1 yamt 207 1.1 yamt dpv[n] = NULL; 208 1.1 yamt xp = NULL; 209 1.1 yamt 210 1.1 yamt /* 211 1.1 yamt * Scan through the buffer in sort 212 1.1 yamt * order, zapping the inode number 213 1.1 yamt * of any duplicate names. 214 1.1 yamt */ 215 1.1 yamt for (n = 0; dpv[n]; n++) { 216 1.1 yamt struct dirent *dp = dpv[n]; 217 1.1 yamt 218 1.1 yamt if ((xp == NULL) || 219 1.1 yamt strcmp(dp->d_name, 220 1.1 yamt xp->d_name)) 221 1.1 yamt xp = dp; 222 1.1 yamt else 223 1.1 yamt dp->d_fileno = 0; 224 1.1 yamt if (dp->d_type == DT_WHT && 225 1.1 yamt (flags & DTF_HIDEW)) 226 1.1 yamt dp->d_fileno = 0; 227 1.1 yamt } 228 1.1 yamt 229 1.1 yamt free(dpv); 230 1.1 yamt break; 231 1.1 yamt } else { 232 1.1 yamt dpv = malloc((n + 1) * 233 1.1 yamt sizeof(struct dirent *)); 234 1.1 yamt if (dpv == NULL) 235 1.1 yamt break; 236 1.1 yamt } 237 1.1 yamt } 238 1.1 yamt } 239 1.1 yamt 240 1.3 christos _DIAGASSERT(__type_fit(int, len)); 241 1.3 christos dirp->dd_len = (int)len; 242 1.1 yamt dirp->dd_size = ddptr - dirp->dd_buf; 243 1.1 yamt } else { 244 1.1 yamt dirp->dd_len = incr; 245 1.5 kre dirp->dd_size = 0; 246 1.1 yamt dirp->dd_buf = malloc((size_t)dirp->dd_len); 247 1.1 yamt if (dirp->dd_buf == NULL) 248 1.1 yamt return errno; 249 1.1 yamt dirp->dd_seek = 0; 250 1.1 yamt flags &= ~DTF_REWIND; 251 1.1 yamt } 252 1.1 yamt dirp->dd_loc = 0; 253 1.1 yamt dirp->dd_fd = fd; 254 1.1 yamt dirp->dd_flags = flags; 255 1.1 yamt /* 256 1.1 yamt * Set up seek point for rewinddir. 257 1.1 yamt */ 258 1.1 yamt (void)_telldir_unlocked(dirp); 259 1.1 yamt return 0; 260 1.1 yamt } 261 1.1 yamt 262 1.1 yamt void 263 1.1 yamt _finidir(DIR *dirp) 264 1.1 yamt { 265 1.1 yamt struct dirpos *poslist; 266 1.1 yamt 267 1.1 yamt free(dirp->dd_buf); 268 1.1 yamt 269 1.1 yamt /* free seekdir/telldir storage */ 270 1.1 yamt for (poslist = dirp->dd_internal; poslist; ) { 271 1.1 yamt struct dirpos *nextpos = poslist->dp_next; 272 1.1 yamt free(poslist); 273 1.1 yamt poslist = nextpos; 274 1.1 yamt } 275 1.1 yamt dirp->dd_internal = NULL; 276 1.1 yamt } 277