| File: | src/lib/libc/gen/fts.c |
| Warning: | line 762, column 7 Dereference of undefined pointer value (loaded from variable 'cp') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: fts.c,v 1.61 2021/11/29 03:20:37 deraadt Exp $ */ | ||||
| 2 | |||||
| 3 | /*- | ||||
| 4 | * Copyright (c) 1990, 1993, 1994 | ||||
| 5 | * The Regents of the University of California. All rights reserved. | ||||
| 6 | * | ||||
| 7 | * Redistribution and use in source and binary forms, with or without | ||||
| 8 | * modification, are permitted provided that the following conditions | ||||
| 9 | * are met: | ||||
| 10 | * 1. Redistributions of source code must retain the above copyright | ||||
| 11 | * notice, this list of conditions and the following disclaimer. | ||||
| 12 | * 2. Redistributions in binary form must reproduce the above copyright | ||||
| 13 | * notice, this list of conditions and the following disclaimer in the | ||||
| 14 | * documentation and/or other materials provided with the distribution. | ||||
| 15 | * 3. Neither the name of the University nor the names of its contributors | ||||
| 16 | * may be used to endorse or promote products derived from this software | ||||
| 17 | * without specific prior written permission. | ||||
| 18 | * | ||||
| 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||||
| 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
| 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||||
| 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
| 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||
| 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||
| 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||
| 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
| 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
| 29 | * SUCH DAMAGE. | ||||
| 30 | */ | ||||
| 31 | |||||
| 32 | #include <sys/param.h> /* ALIGN ALIGNBYTES */ | ||||
| 33 | #include <sys/stat.h> | ||||
| 34 | |||||
| 35 | #include <dirent.h> | ||||
| 36 | #include <errno(*__errno()).h> | ||||
| 37 | #include <fcntl.h> | ||||
| 38 | #include <fts.h> | ||||
| 39 | #include <limits.h> | ||||
| 40 | #include <stdlib.h> | ||||
| 41 | #include <string.h> | ||||
| 42 | #include <unistd.h> | ||||
| 43 | |||||
| 44 | #define MAXIMUM(a, b)(((a) > (b)) ? (a) : (b)) (((a) > (b)) ? (a) : (b)) | ||||
| 45 | |||||
| 46 | static FTSENT *fts_alloc(FTS *, const char *, size_t); | ||||
| 47 | static FTSENT *fts_build(FTS *, int); | ||||
| 48 | static void fts_lfree(FTSENT *); | ||||
| 49 | static void fts_load(FTS *, FTSENT *); | ||||
| 50 | static size_t fts_maxarglen(char * const *); | ||||
| 51 | static void fts_padjust(FTS *, FTSENT *); | ||||
| 52 | static int fts_palloc(FTS *, size_t); | ||||
| 53 | static FTSENT *fts_sort(FTS *, FTSENT *, int); | ||||
| 54 | static u_short fts_stat(FTS *, FTSENT *, int, int); | ||||
| 55 | static int fts_safe_changedir(FTS *, FTSENT *, int, const char *); | ||||
| 56 | |||||
| 57 | #define ISDOT(a)(a[0] == '.' && (!a[1] || (a[1] == '.' && !a[ 2]))) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) | ||||
| 58 | |||||
| 59 | #define CLR(opt)(sp->fts_options &= ~(opt)) (sp->fts_options &= ~(opt)) | ||||
| 60 | #define ISSET(opt)(sp->fts_options & (opt)) (sp->fts_options & (opt)) | ||||
| 61 | #define SET(opt)(sp->fts_options |= (opt)) (sp->fts_options |= (opt)) | ||||
| 62 | |||||
| 63 | #define FCHDIR(sp, fd)(!(sp->fts_options & (0x0004)) && fchdir(fd)) (!ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004)) && fchdir(fd)) | ||||
| 64 | |||||
| 65 | /* fts_build flags */ | ||||
| 66 | #define BCHILD1 1 /* fts_children */ | ||||
| 67 | #define BNAMES2 2 /* fts_children, names only */ | ||||
| 68 | #define BREAD3 3 /* fts_read */ | ||||
| 69 | |||||
| 70 | FTS * | ||||
| 71 | fts_open(char * const *argv, int options, | ||||
| 72 | int (*compar)(const FTSENT **, const FTSENT **)) | ||||
| 73 | { | ||||
| 74 | FTS *sp; | ||||
| 75 | FTSENT *p, *root; | ||||
| 76 | int nitems; | ||||
| 77 | FTSENT *parent, *prev; | ||||
| 78 | |||||
| 79 | /* Options check. */ | ||||
| 80 | if (options & ~FTS_OPTIONMASK0x00ff) { | ||||
| 81 | errno(*__errno()) = EINVAL22; | ||||
| 82 | return (NULL((void *)0)); | ||||
| 83 | } | ||||
| 84 | |||||
| 85 | /* At least one path must be specified. */ | ||||
| 86 | if (*argv == NULL((void *)0)) { | ||||
| 87 | errno(*__errno()) = EINVAL22; | ||||
| 88 | return (NULL((void *)0)); | ||||
| 89 | } | ||||
| 90 | |||||
| 91 | /* Allocate/initialize the stream */ | ||||
| 92 | if ((sp = calloc(1, sizeof(FTS))) == NULL((void *)0)) | ||||
| 93 | return (NULL((void *)0)); | ||||
| 94 | sp->fts_compar = compar; | ||||
| 95 | sp->fts_options = options; | ||||
| 96 | |||||
| 97 | /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ | ||||
| 98 | if (ISSET(FTS_LOGICAL)(sp->fts_options & (0x0002))) | ||||
| 99 | SET(FTS_NOCHDIR)(sp->fts_options |= (0x0004)); | ||||
| 100 | |||||
| 101 | /* | ||||
| 102 | * Start out with 1K of path space, and enough, in any case, | ||||
| 103 | * to hold the user's paths. | ||||
| 104 | */ | ||||
| 105 | if (fts_palloc(sp, MAXIMUM(fts_maxarglen(argv), PATH_MAX)(((fts_maxarglen(argv)) > (1024)) ? (fts_maxarglen(argv)) : (1024)))) | ||||
| 106 | goto mem1; | ||||
| 107 | |||||
| 108 | /* Allocate/initialize root's parent. */ | ||||
| 109 | if ((parent = fts_alloc(sp, "", 0)) == NULL((void *)0)) | ||||
| 110 | goto mem2; | ||||
| 111 | parent->fts_level = FTS_ROOTPARENTLEVEL-1; | ||||
| 112 | |||||
| 113 | /* Allocate/initialize root(s). */ | ||||
| 114 | for (root = prev = NULL((void *)0), nitems = 0; *argv; ++argv, ++nitems) { | ||||
| 115 | if ((p = fts_alloc(sp, *argv, strlen(*argv))) == NULL((void *)0)) | ||||
| 116 | goto mem3; | ||||
| 117 | p->fts_level = FTS_ROOTLEVEL0; | ||||
| 118 | p->fts_parent = parent; | ||||
| 119 | p->fts_accpath = p->fts_name; | ||||
| 120 | p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)(sp->fts_options & (0x0001)), -1); | ||||
| 121 | |||||
| 122 | /* Command-line "." and ".." are real directories. */ | ||||
| 123 | if (p->fts_info == FTS_DOT5) | ||||
| 124 | p->fts_info = FTS_D1; | ||||
| 125 | |||||
| 126 | /* | ||||
| 127 | * If comparison routine supplied, traverse in sorted | ||||
| 128 | * order; otherwise traverse in the order specified. | ||||
| 129 | */ | ||||
| 130 | if (compar) { | ||||
| 131 | p->fts_link = root; | ||||
| 132 | root = p; | ||||
| 133 | } else { | ||||
| 134 | p->fts_link = NULL((void *)0); | ||||
| 135 | if (root == NULL((void *)0)) | ||||
| 136 | root = p; | ||||
| 137 | else | ||||
| 138 | prev->fts_link = p; | ||||
| 139 | prev = p; | ||||
| 140 | } | ||||
| 141 | } | ||||
| 142 | if (compar && nitems > 1) | ||||
| 143 | root = fts_sort(sp, root, nitems); | ||||
| 144 | |||||
| 145 | /* | ||||
| 146 | * Allocate a dummy pointer and make fts_read think that we've just | ||||
| 147 | * finished the node before the root(s); set p->fts_info to FTS_INIT | ||||
| 148 | * so that everything about the "current" node is ignored. | ||||
| 149 | */ | ||||
| 150 | if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL((void *)0)) | ||||
| 151 | goto mem3; | ||||
| 152 | sp->fts_cur->fts_link = root; | ||||
| 153 | sp->fts_cur->fts_info = FTS_INIT9; | ||||
| 154 | |||||
| 155 | /* | ||||
| 156 | * If using chdir(2), grab a file descriptor pointing to dot to ensure | ||||
| 157 | * that we can get back here; this could be avoided for some paths, | ||||
| 158 | * but almost certainly not worth the effort. Slashes, symbolic links, | ||||
| 159 | * and ".." are all fairly nasty problems. Note, if we can't get the | ||||
| 160 | * descriptor we run anyway, just more slowly. | ||||
| 161 | */ | ||||
| 162 | if (!ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004)) && | ||||
| 163 | (sp->fts_rfd = open(".", O_RDONLY0x0000 | O_CLOEXEC0x10000)) == -1) | ||||
| 164 | SET(FTS_NOCHDIR)(sp->fts_options |= (0x0004)); | ||||
| 165 | |||||
| 166 | if (nitems == 0) | ||||
| 167 | free(parent); | ||||
| 168 | |||||
| 169 | return (sp); | ||||
| 170 | |||||
| 171 | mem3: fts_lfree(root); | ||||
| 172 | free(parent); | ||||
| 173 | mem2: free(sp->fts_path); | ||||
| 174 | mem1: free(sp); | ||||
| 175 | return (NULL((void *)0)); | ||||
| 176 | } | ||||
| 177 | DEF_WEAK(fts_open)__asm__(".weak " "fts_open" " ; " "fts_open" " = " "_libc_fts_open" ); | ||||
| 178 | |||||
| 179 | static void | ||||
| 180 | fts_load(FTS *sp, FTSENT *p) | ||||
| 181 | { | ||||
| 182 | size_t len; | ||||
| 183 | char *cp; | ||||
| 184 | |||||
| 185 | /* | ||||
| 186 | * Load the stream structure for the next traversal. Since we don't | ||||
| 187 | * actually enter the directory until after the preorder visit, set | ||||
| 188 | * the fts_accpath field specially so the chdir gets done to the right | ||||
| 189 | * place and the user can access the first node. From fts_open it's | ||||
| 190 | * known that the path will fit. | ||||
| 191 | */ | ||||
| 192 | len = p->fts_pathlen = p->fts_namelen; | ||||
| 193 | memmove(sp->fts_path, p->fts_name, len + 1); | ||||
| 194 | if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { | ||||
| 195 | len = strlen(++cp); | ||||
| 196 | memmove(p->fts_name, cp, len + 1); | ||||
| 197 | p->fts_namelen = len; | ||||
| 198 | } | ||||
| 199 | p->fts_accpath = p->fts_path = sp->fts_path; | ||||
| 200 | sp->fts_dev = p->fts_dev; | ||||
| 201 | } | ||||
| 202 | |||||
| 203 | int | ||||
| 204 | fts_close(FTS *sp) | ||||
| 205 | { | ||||
| 206 | FTSENT *freep, *p; | ||||
| 207 | int rfd, error = 0; | ||||
| 208 | |||||
| 209 | /* | ||||
| 210 | * This still works if we haven't read anything -- the dummy structure | ||||
| 211 | * points to the root list, so we step through to the end of the root | ||||
| 212 | * list which has a valid parent pointer. | ||||
| 213 | */ | ||||
| 214 | if (sp->fts_cur) { | ||||
| 215 | for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL0;) { | ||||
| 216 | freep = p; | ||||
| 217 | p = p->fts_link ? p->fts_link : p->fts_parent; | ||||
| 218 | free(freep); | ||||
| 219 | } | ||||
| 220 | free(p); | ||||
| 221 | } | ||||
| 222 | |||||
| 223 | /* Stash the original directory fd if needed. */ | ||||
| 224 | rfd = ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004)) ? -1 : sp->fts_rfd; | ||||
| 225 | |||||
| 226 | /* Free up child linked list, sort array, path buffer, stream ptr.*/ | ||||
| 227 | if (sp->fts_child) | ||||
| 228 | fts_lfree(sp->fts_child); | ||||
| 229 | free(sp->fts_array); | ||||
| 230 | free(sp->fts_path); | ||||
| 231 | free(sp); | ||||
| 232 | |||||
| 233 | /* Return to original directory, checking for error. */ | ||||
| 234 | if (rfd != -1) { | ||||
| 235 | int saved_errno; | ||||
| 236 | error = fchdir(rfd); | ||||
| 237 | saved_errno = errno(*__errno()); | ||||
| 238 | (void)close(rfd); | ||||
| 239 | errno(*__errno()) = saved_errno; | ||||
| 240 | } | ||||
| 241 | |||||
| 242 | return (error); | ||||
| 243 | } | ||||
| 244 | DEF_WEAK(fts_close)__asm__(".weak " "fts_close" " ; " "fts_close" " = " "_libc_fts_close" ); | ||||
| 245 | |||||
| 246 | /* | ||||
| 247 | * Special case of "/" at the end of the path so that slashes aren't | ||||
| 248 | * appended which would cause paths to be written as "....//foo". | ||||
| 249 | */ | ||||
| 250 | #define NAPPEND(p)(p->fts_path[p->fts_pathlen - 1] == '/' ? p->fts_pathlen - 1 : p->fts_pathlen) \ | ||||
| 251 | (p->fts_path[p->fts_pathlen - 1] == '/' \ | ||||
| 252 | ? p->fts_pathlen - 1 : p->fts_pathlen) | ||||
| 253 | |||||
| 254 | FTSENT * | ||||
| 255 | fts_read(FTS *sp) | ||||
| 256 | { | ||||
| 257 | FTSENT *p, *tmp; | ||||
| 258 | int instr; | ||||
| 259 | char *t; | ||||
| 260 | int saved_errno; | ||||
| 261 | |||||
| 262 | /* If finished or unrecoverable error, return NULL. */ | ||||
| 263 | if (sp->fts_cur == NULL((void *)0) || ISSET(FTS_STOP)(sp->fts_options & (0x2000))) | ||||
| 264 | return (NULL((void *)0)); | ||||
| 265 | |||||
| 266 | /* Set current node pointer. */ | ||||
| 267 | p = sp->fts_cur; | ||||
| 268 | |||||
| 269 | /* Save and zero out user instructions. */ | ||||
| 270 | instr = p->fts_instr; | ||||
| 271 | p->fts_instr = FTS_NOINSTR3; | ||||
| 272 | |||||
| 273 | /* Any type of file may be re-visited; re-stat and re-turn. */ | ||||
| 274 | if (instr == FTS_AGAIN1) { | ||||
| 275 | p->fts_info = fts_stat(sp, p, 0, -1); | ||||
| 276 | return (p); | ||||
| 277 | } | ||||
| 278 | |||||
| 279 | /* | ||||
| 280 | * Following a symlink -- SLNONE test allows application to see | ||||
| 281 | * SLNONE and recover. If indirecting through a symlink, have | ||||
| 282 | * keep a pointer to current location. If unable to get that | ||||
| 283 | * pointer, follow fails. | ||||
| 284 | */ | ||||
| 285 | if (instr == FTS_FOLLOW2 && | ||||
| 286 | (p->fts_info == FTS_SL12 || p->fts_info == FTS_SLNONE13)) { | ||||
| 287 | p->fts_info = fts_stat(sp, p, 1, -1); | ||||
| 288 | if (p->fts_info == FTS_D1 && !ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) { | ||||
| 289 | if ((p->fts_symfd = | ||||
| 290 | open(".", O_RDONLY0x0000 | O_CLOEXEC0x10000)) == -1) { | ||||
| 291 | p->fts_errno = errno(*__errno()); | ||||
| 292 | p->fts_info = FTS_ERR7; | ||||
| 293 | } else | ||||
| 294 | p->fts_flags |= FTS_SYMFOLLOW0x02; | ||||
| 295 | } | ||||
| 296 | return (p); | ||||
| 297 | } | ||||
| 298 | |||||
| 299 | /* Directory in pre-order. */ | ||||
| 300 | if (p->fts_info == FTS_D1) { | ||||
| 301 | /* If skipped or crossed mount point, do post-order visit. */ | ||||
| 302 | if (instr == FTS_SKIP4 || | ||||
| 303 | (ISSET(FTS_XDEV)(sp->fts_options & (0x0040)) && p->fts_dev != sp->fts_dev)) { | ||||
| 304 | if (p->fts_flags & FTS_SYMFOLLOW0x02) | ||||
| 305 | (void)close(p->fts_symfd); | ||||
| 306 | if (sp->fts_child) { | ||||
| 307 | fts_lfree(sp->fts_child); | ||||
| 308 | sp->fts_child = NULL((void *)0); | ||||
| 309 | } | ||||
| 310 | p->fts_info = FTS_DP6; | ||||
| 311 | return (p); | ||||
| 312 | } | ||||
| 313 | |||||
| 314 | /* Rebuild if only read the names and now traversing. */ | ||||
| 315 | if (sp->fts_child && ISSET(FTS_NAMEONLY)(sp->fts_options & (0x1000))) { | ||||
| 316 | CLR(FTS_NAMEONLY)(sp->fts_options &= ~(0x1000)); | ||||
| 317 | fts_lfree(sp->fts_child); | ||||
| 318 | sp->fts_child = NULL((void *)0); | ||||
| 319 | } | ||||
| 320 | |||||
| 321 | /* | ||||
| 322 | * Cd to the subdirectory. | ||||
| 323 | * | ||||
| 324 | * If have already read and now fail to chdir, whack the list | ||||
| 325 | * to make the names come out right, and set the parent errno | ||||
| 326 | * so the application will eventually get an error condition. | ||||
| 327 | * Set the FTS_DONTCHDIR flag so that when we logically change | ||||
| 328 | * directories back to the parent we don't do a chdir. | ||||
| 329 | * | ||||
| 330 | * If haven't read do so. If the read fails, fts_build sets | ||||
| 331 | * FTS_STOP or the fts_info field of the node. | ||||
| 332 | */ | ||||
| 333 | if (sp->fts_child) { | ||||
| 334 | if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { | ||||
| 335 | p->fts_errno = errno(*__errno()); | ||||
| 336 | p->fts_flags |= FTS_DONTCHDIR0x01; | ||||
| 337 | for (p = sp->fts_child; p; p = p->fts_link) | ||||
| 338 | p->fts_accpath = | ||||
| 339 | p->fts_parent->fts_accpath; | ||||
| 340 | } | ||||
| 341 | } else if ((sp->fts_child = fts_build(sp, BREAD3)) == NULL((void *)0)) { | ||||
| 342 | if (ISSET(FTS_STOP)(sp->fts_options & (0x2000))) | ||||
| 343 | return (NULL((void *)0)); | ||||
| 344 | return (p); | ||||
| 345 | } | ||||
| 346 | p = sp->fts_child; | ||||
| 347 | sp->fts_child = NULL((void *)0); | ||||
| 348 | goto name; | ||||
| 349 | } | ||||
| 350 | |||||
| 351 | /* Move to the next node on this level. */ | ||||
| 352 | next: tmp = p; | ||||
| 353 | if ((p = p->fts_link)) { | ||||
| 354 | free(tmp); | ||||
| 355 | |||||
| 356 | /* | ||||
| 357 | * If reached the top, return to the original directory (or | ||||
| 358 | * the root of the tree), and load the paths for the next root. | ||||
| 359 | */ | ||||
| 360 | if (p->fts_level == FTS_ROOTLEVEL0) { | ||||
| 361 | if (FCHDIR(sp, sp->fts_rfd)(!(sp->fts_options & (0x0004)) && fchdir(sp-> fts_rfd))) { | ||||
| 362 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
| 363 | return (NULL((void *)0)); | ||||
| 364 | } | ||||
| 365 | fts_load(sp, p); | ||||
| 366 | return (sp->fts_cur = p); | ||||
| 367 | } | ||||
| 368 | |||||
| 369 | /* | ||||
| 370 | * User may have called fts_set on the node. If skipped, | ||||
| 371 | * ignore. If followed, get a file descriptor so we can | ||||
| 372 | * get back if necessary. | ||||
| 373 | */ | ||||
| 374 | if (p->fts_instr == FTS_SKIP4) | ||||
| 375 | goto next; | ||||
| 376 | if (p->fts_instr == FTS_FOLLOW2) { | ||||
| 377 | p->fts_info = fts_stat(sp, p, 1, -1); | ||||
| 378 | if (p->fts_info == FTS_D1 && !ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) { | ||||
| 379 | if ((p->fts_symfd = | ||||
| 380 | open(".", O_RDONLY0x0000 | O_CLOEXEC0x10000)) == -1) { | ||||
| 381 | p->fts_errno = errno(*__errno()); | ||||
| 382 | p->fts_info = FTS_ERR7; | ||||
| 383 | } else | ||||
| 384 | p->fts_flags |= FTS_SYMFOLLOW0x02; | ||||
| 385 | } | ||||
| 386 | p->fts_instr = FTS_NOINSTR3; | ||||
| 387 | } | ||||
| 388 | |||||
| 389 | name: t = sp->fts_path + NAPPEND(p->fts_parent)(p->fts_parent->fts_path[p->fts_parent->fts_pathlen - 1] == '/' ? p->fts_parent->fts_pathlen - 1 : p->fts_parent ->fts_pathlen); | ||||
| 390 | *t++ = '/'; | ||||
| 391 | memmove(t, p->fts_name, p->fts_namelen + 1); | ||||
| 392 | return (sp->fts_cur = p); | ||||
| 393 | } | ||||
| 394 | |||||
| 395 | /* Move up to the parent node. */ | ||||
| 396 | p = tmp->fts_parent; | ||||
| 397 | free(tmp); | ||||
| 398 | |||||
| 399 | if (p->fts_level == FTS_ROOTPARENTLEVEL-1) { | ||||
| 400 | /* | ||||
| 401 | * Done; free everything up and set errno to 0 so the user | ||||
| 402 | * can distinguish between error and EOF. | ||||
| 403 | */ | ||||
| 404 | free(p); | ||||
| 405 | errno(*__errno()) = 0; | ||||
| 406 | return (sp->fts_cur = NULL((void *)0)); | ||||
| 407 | } | ||||
| 408 | |||||
| 409 | /* NUL terminate the pathname. */ | ||||
| 410 | sp->fts_path[p->fts_pathlen] = '\0'; | ||||
| 411 | |||||
| 412 | /* | ||||
| 413 | * Return to the parent directory. If at a root node or came through | ||||
| 414 | * a symlink, go back through the file descriptor. Otherwise, cd up | ||||
| 415 | * one directory. | ||||
| 416 | */ | ||||
| 417 | if (p->fts_level == FTS_ROOTLEVEL0) { | ||||
| 418 | if (FCHDIR(sp, sp->fts_rfd)(!(sp->fts_options & (0x0004)) && fchdir(sp-> fts_rfd))) { | ||||
| 419 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
| 420 | sp->fts_cur = p; | ||||
| 421 | return (NULL((void *)0)); | ||||
| 422 | } | ||||
| 423 | } else if (p->fts_flags & FTS_SYMFOLLOW0x02) { | ||||
| 424 | if (FCHDIR(sp, p->fts_symfd)(!(sp->fts_options & (0x0004)) && fchdir(p-> fts_symfd))) { | ||||
| 425 | saved_errno = errno(*__errno()); | ||||
| 426 | (void)close(p->fts_symfd); | ||||
| 427 | errno(*__errno()) = saved_errno; | ||||
| 428 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
| 429 | sp->fts_cur = p; | ||||
| 430 | return (NULL((void *)0)); | ||||
| 431 | } | ||||
| 432 | (void)close(p->fts_symfd); | ||||
| 433 | } else if (!(p->fts_flags & FTS_DONTCHDIR0x01) && | ||||
| 434 | fts_safe_changedir(sp, p->fts_parent, -1, "..")) { | ||||
| 435 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
| 436 | sp->fts_cur = p; | ||||
| 437 | return (NULL((void *)0)); | ||||
| 438 | } | ||||
| 439 | p->fts_info = p->fts_errno ? FTS_ERR7 : FTS_DP6; | ||||
| 440 | return (sp->fts_cur = p); | ||||
| 441 | } | ||||
| 442 | DEF_WEAK(fts_read)__asm__(".weak " "fts_read" " ; " "fts_read" " = " "_libc_fts_read" ); | ||||
| 443 | |||||
| 444 | /* | ||||
| 445 | * Fts_set takes the stream as an argument although it's not used in this | ||||
| 446 | * implementation; it would be necessary if anyone wanted to add global | ||||
| 447 | * semantics to fts using fts_set. An error return is allowed for similar | ||||
| 448 | * reasons. | ||||
| 449 | */ | ||||
| 450 | int | ||||
| 451 | fts_set(FTS *sp, FTSENT *p, int instr) | ||||
| 452 | { | ||||
| 453 | if (instr && instr != FTS_AGAIN1 && instr != FTS_FOLLOW2 && | ||||
| 454 | instr != FTS_NOINSTR3 && instr != FTS_SKIP4) { | ||||
| 455 | errno(*__errno()) = EINVAL22; | ||||
| 456 | return (1); | ||||
| 457 | } | ||||
| 458 | p->fts_instr = instr; | ||||
| 459 | return (0); | ||||
| 460 | } | ||||
| 461 | DEF_WEAK(fts_set)__asm__(".weak " "fts_set" " ; " "fts_set" " = " "_libc_fts_set" ); | ||||
| 462 | |||||
| 463 | FTSENT * | ||||
| 464 | fts_children(FTS *sp, int instr) | ||||
| 465 | { | ||||
| 466 | FTSENT *p; | ||||
| 467 | int fd; | ||||
| 468 | |||||
| 469 | if (instr && instr != FTS_NAMEONLY0x1000) { | ||||
| 470 | errno(*__errno()) = EINVAL22; | ||||
| 471 | return (NULL((void *)0)); | ||||
| 472 | } | ||||
| 473 | |||||
| 474 | /* Set current node pointer. */ | ||||
| 475 | p = sp->fts_cur; | ||||
| 476 | |||||
| 477 | /* | ||||
| 478 | * Errno set to 0 so user can distinguish empty directory from | ||||
| 479 | * an error. | ||||
| 480 | */ | ||||
| 481 | errno(*__errno()) = 0; | ||||
| 482 | |||||
| 483 | /* Fatal errors stop here. */ | ||||
| 484 | if (ISSET(FTS_STOP)(sp->fts_options & (0x2000))) | ||||
| 485 | return (NULL((void *)0)); | ||||
| 486 | |||||
| 487 | /* Return logical hierarchy of user's arguments. */ | ||||
| 488 | if (p->fts_info == FTS_INIT9) | ||||
| 489 | return (p->fts_link); | ||||
| 490 | |||||
| 491 | /* | ||||
| 492 | * If not a directory being visited in pre-order, stop here. Could | ||||
| 493 | * allow FTS_DNR, assuming the user has fixed the problem, but the | ||||
| 494 | * same effect is available with FTS_AGAIN. | ||||
| 495 | */ | ||||
| 496 | if (p->fts_info != FTS_D1 /* && p->fts_info != FTS_DNR */) | ||||
| 497 | return (NULL((void *)0)); | ||||
| 498 | |||||
| 499 | /* Free up any previous child list. */ | ||||
| 500 | if (sp->fts_child) | ||||
| 501 | fts_lfree(sp->fts_child); | ||||
| 502 | |||||
| 503 | if (instr == FTS_NAMEONLY0x1000) { | ||||
| 504 | SET(FTS_NAMEONLY)(sp->fts_options |= (0x1000)); | ||||
| 505 | instr = BNAMES2; | ||||
| 506 | } else | ||||
| 507 | instr = BCHILD1; | ||||
| 508 | |||||
| 509 | /* | ||||
| 510 | * If using chdir on a relative path and called BEFORE fts_read does | ||||
| 511 | * its chdir to the root of a traversal, we can lose -- we need to | ||||
| 512 | * chdir into the subdirectory, and we don't know where the current | ||||
| 513 | * directory is, so we can't get back so that the upcoming chdir by | ||||
| 514 | * fts_read will work. | ||||
| 515 | */ | ||||
| 516 | if (p->fts_level != FTS_ROOTLEVEL0 || p->fts_accpath[0] == '/' || | ||||
| 517 | ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) | ||||
| 518 | return (sp->fts_child = fts_build(sp, instr)); | ||||
| 519 | |||||
| 520 | if ((fd = open(".", O_RDONLY0x0000 | O_CLOEXEC0x10000)) == -1) | ||||
| 521 | return (NULL((void *)0)); | ||||
| 522 | sp->fts_child = fts_build(sp, instr); | ||||
| 523 | if (fchdir(fd)) { | ||||
| 524 | (void)close(fd); | ||||
| 525 | return (NULL((void *)0)); | ||||
| 526 | } | ||||
| 527 | (void)close(fd); | ||||
| 528 | return (sp->fts_child); | ||||
| 529 | } | ||||
| 530 | DEF_WEAK(fts_children)__asm__(".weak " "fts_children" " ; " "fts_children" " = " "_libc_fts_children" ); | ||||
| 531 | |||||
| 532 | /* | ||||
| 533 | * This is the tricky part -- do not casually change *anything* in here. The | ||||
| 534 | * idea is to build the linked list of entries that are used by fts_children | ||||
| 535 | * and fts_read. There are lots of special cases. | ||||
| 536 | * | ||||
| 537 | * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is | ||||
| 538 | * set and it's a physical walk (so that symbolic links can't be directories), | ||||
| 539 | * we can do things quickly. First, if it's a 4.4BSD file system, the type | ||||
| 540 | * of the file is in the directory entry. Otherwise, we assume that the number | ||||
| 541 | * of subdirectories in a node is equal to the number of links to the parent. | ||||
| 542 | * The former skips all stat calls. The latter skips stat calls in any leaf | ||||
| 543 | * directories and for any files after the subdirectories in the directory have | ||||
| 544 | * been found, cutting the stat calls by about 2/3. | ||||
| 545 | */ | ||||
| 546 | static FTSENT * | ||||
| 547 | fts_build(FTS *sp, int type) | ||||
| 548 | { | ||||
| 549 | struct dirent *dp; | ||||
| 550 | FTSENT *p, *head; | ||||
| 551 | FTSENT *cur, *tail; | ||||
| 552 | DIR *dirp; | ||||
| 553 | void *oldaddr; | ||||
| 554 | size_t len, maxlen; | ||||
| 555 | int nitems, cderrno, descend, level, nlinks, nostat, doadjust; | ||||
| 556 | int saved_errno; | ||||
| 557 | char *cp; | ||||
| |||||
| 558 | |||||
| 559 | /* Set current node pointer. */ | ||||
| 560 | cur = sp->fts_cur; | ||||
| 561 | |||||
| 562 | /* | ||||
| 563 | * Open the directory for reading. If this fails, we're done. | ||||
| 564 | * If being called from fts_read, set the fts_info field. | ||||
| 565 | */ | ||||
| 566 | if ((dirp = opendir(cur->fts_accpath)) == NULL((void *)0)) { | ||||
| 567 | if (type == BREAD3) { | ||||
| 568 | cur->fts_info = FTS_DNR4; | ||||
| 569 | cur->fts_errno = errno(*__errno()); | ||||
| 570 | } | ||||
| 571 | return (NULL((void *)0)); | ||||
| 572 | } | ||||
| 573 | |||||
| 574 | /* | ||||
| 575 | * Nlinks is the number of possible entries of type directory in the | ||||
| 576 | * directory if we're cheating on stat calls, 0 if we're not doing | ||||
| 577 | * any stat calls at all, -1 if we're doing stats on everything. | ||||
| 578 | */ | ||||
| 579 | if (type == BNAMES2) | ||||
| 580 | nlinks = 0; | ||||
| 581 | else if (ISSET(FTS_NOSTAT)(sp->fts_options & (0x0008)) && ISSET(FTS_PHYSICAL)(sp->fts_options & (0x0010))) { | ||||
| 582 | nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT)(sp->fts_options & (0x0020)) ? 0 : 2); | ||||
| 583 | nostat = 1; | ||||
| 584 | } else { | ||||
| 585 | nlinks = -1; | ||||
| 586 | nostat = 0; | ||||
| 587 | } | ||||
| 588 | |||||
| 589 | #ifdef notdef | ||||
| 590 | (void)printf("nlinks == %d (cur: %u)\n", nlinks, cur->fts_nlink); | ||||
| 591 | (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", | ||||
| 592 | ISSET(FTS_NOSTAT)(sp->fts_options & (0x0008)), ISSET(FTS_PHYSICAL)(sp->fts_options & (0x0010)), ISSET(FTS_SEEDOT)(sp->fts_options & (0x0020))); | ||||
| 593 | #endif | ||||
| 594 | /* | ||||
| 595 | * If we're going to need to stat anything or we want to descend | ||||
| 596 | * and stay in the directory, chdir. If this fails we keep going, | ||||
| 597 | * but set a flag so we don't chdir after the post-order visit. | ||||
| 598 | * We won't be able to stat anything, but we can still return the | ||||
| 599 | * names themselves. Note, that since fts_read won't be able to | ||||
| 600 | * chdir into the directory, it will have to return different path | ||||
| 601 | * names than before, i.e. "a/b" instead of "b". Since the node | ||||
| 602 | * has already been visited in pre-order, have to wait until the | ||||
| 603 | * post-order visit to return the error. There is a special case | ||||
| 604 | * here, if there was nothing to stat then it's not an error to | ||||
| 605 | * not be able to stat. This is all fairly nasty. If a program | ||||
| 606 | * needed sorted entries or stat information, they had better be | ||||
| 607 | * checking FTS_NS on the returned nodes. | ||||
| 608 | */ | ||||
| 609 | cderrno = 0; | ||||
| 610 | if (nlinks
| ||||
| 611 | if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL((void *)0))) { | ||||
| 612 | if (nlinks && type == BREAD3) | ||||
| 613 | cur->fts_errno = errno(*__errno()); | ||||
| 614 | cur->fts_flags |= FTS_DONTCHDIR0x01; | ||||
| 615 | descend = 0; | ||||
| 616 | cderrno = errno(*__errno()); | ||||
| 617 | (void)closedir(dirp); | ||||
| 618 | dirp = NULL((void *)0); | ||||
| 619 | } else | ||||
| 620 | descend = 1; | ||||
| 621 | } else | ||||
| 622 | descend = 0; | ||||
| 623 | |||||
| 624 | /* | ||||
| 625 | * Figure out the max file name length that can be stored in the | ||||
| 626 | * current path -- the inner loop allocates more path as necessary. | ||||
| 627 | * We really wouldn't have to do the maxlen calculations here, we | ||||
| 628 | * could do them in fts_read before returning the path, but it's a | ||||
| 629 | * lot easier here since the length is part of the dirent structure. | ||||
| 630 | * | ||||
| 631 | * If not changing directories set a pointer so that can just append | ||||
| 632 | * each new name into the path. | ||||
| 633 | */ | ||||
| 634 | len = NAPPEND(cur)(cur->fts_path[cur->fts_pathlen - 1] == '/' ? cur->fts_pathlen - 1 : cur->fts_pathlen); | ||||
| 635 | if (ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) { | ||||
| 636 | cp = sp->fts_path + len; | ||||
| 637 | *cp++ = '/'; | ||||
| 638 | } | ||||
| 639 | len++; | ||||
| 640 | maxlen = sp->fts_pathlen - len; | ||||
| 641 | |||||
| 642 | /* | ||||
| 643 | * fts_level is signed so we must prevent it from wrapping | ||||
| 644 | * around to FTS_ROOTLEVEL and FTS_ROOTPARENTLEVEL. | ||||
| 645 | */ | ||||
| 646 | level = cur->fts_level; | ||||
| 647 | if (level < FTS_MAXLEVEL0x7fffffff) | ||||
| 648 | level++; | ||||
| 649 | |||||
| 650 | /* Read the directory, attaching each entry to the `link' pointer. */ | ||||
| 651 | doadjust = 0; | ||||
| 652 | for (head = tail = NULL((void *)0), nitems = 0; dirp
| ||||
| 653 | if (!ISSET(FTS_SEEDOT)(sp->fts_options & (0x0020)) && ISDOT(dp->d_name)(dp->d_name[0] == '.' && (!dp->d_name[1] || (dp ->d_name[1] == '.' && !dp->d_name[2])))) | ||||
| 654 | continue; | ||||
| 655 | |||||
| 656 | if (!(p = fts_alloc(sp, dp->d_name, dp->d_namlen))) | ||||
| 657 | goto mem1; | ||||
| 658 | if (dp->d_namlen >= maxlen) { /* include space for NUL */ | ||||
| 659 | oldaddr = sp->fts_path; | ||||
| 660 | if (fts_palloc(sp, dp->d_namlen +len + 1)) { | ||||
| 661 | /* | ||||
| 662 | * No more memory for path or structures. Save | ||||
| 663 | * errno, free up the current structure and the | ||||
| 664 | * structures already allocated. | ||||
| 665 | */ | ||||
| 666 | mem1: saved_errno = errno(*__errno()); | ||||
| 667 | free(p); | ||||
| 668 | fts_lfree(head); | ||||
| 669 | (void)closedir(dirp); | ||||
| 670 | cur->fts_info = FTS_ERR7; | ||||
| 671 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
| 672 | errno(*__errno()) = saved_errno; | ||||
| 673 | return (NULL((void *)0)); | ||||
| 674 | } | ||||
| 675 | /* Did realloc() change the pointer? */ | ||||
| 676 | if (oldaddr != sp->fts_path) { | ||||
| 677 | doadjust = 1; | ||||
| 678 | if (ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) | ||||
| 679 | cp = sp->fts_path + len; | ||||
| 680 | } | ||||
| 681 | maxlen = sp->fts_pathlen - len; | ||||
| 682 | } | ||||
| 683 | |||||
| 684 | p->fts_level = level; | ||||
| 685 | p->fts_parent = sp->fts_cur; | ||||
| 686 | p->fts_pathlen = len + dp->d_namlen; | ||||
| 687 | if (p->fts_pathlen < len) { | ||||
| 688 | /* | ||||
| 689 | * If we wrap, free up the current structure and | ||||
| 690 | * the structures already allocated, then error | ||||
| 691 | * out with ENAMETOOLONG. | ||||
| 692 | */ | ||||
| 693 | free(p); | ||||
| 694 | fts_lfree(head); | ||||
| 695 | (void)closedir(dirp); | ||||
| 696 | cur->fts_info = FTS_ERR7; | ||||
| 697 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
| 698 | errno(*__errno()) = ENAMETOOLONG63; | ||||
| 699 | return (NULL((void *)0)); | ||||
| 700 | } | ||||
| 701 | |||||
| 702 | if (cderrno
| ||||
| 703 | if (nlinks) { | ||||
| 704 | p->fts_info = FTS_NS10; | ||||
| 705 | p->fts_errno = cderrno; | ||||
| 706 | } else | ||||
| 707 | p->fts_info = FTS_NSOK11; | ||||
| 708 | p->fts_accpath = cur->fts_accpath; | ||||
| 709 | } else if (nlinks
| ||||
| 710 | #ifdef DT_DIR4 | ||||
| 711 | || (nostat
| ||||
| 712 | dp->d_type != DT_DIR4 && dp->d_type != DT_UNKNOWN0) | ||||
| 713 | #endif | ||||
| 714 | ) { | ||||
| 715 | p->fts_accpath = | ||||
| 716 | ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004)) ? p->fts_path : p->fts_name; | ||||
| 717 | p->fts_info = FTS_NSOK11; | ||||
| 718 | } else { | ||||
| 719 | /* Build a file name for fts_stat to stat. */ | ||||
| 720 | if (ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) { | ||||
| 721 | p->fts_accpath = p->fts_path; | ||||
| 722 | memmove(cp, p->fts_name, p->fts_namelen + 1); | ||||
| 723 | p->fts_info = fts_stat(sp, p, 0, dirfd(dirp)); | ||||
| 724 | } else { | ||||
| 725 | p->fts_accpath = p->fts_name; | ||||
| 726 | p->fts_info = fts_stat(sp, p, 0, -1); | ||||
| 727 | } | ||||
| 728 | |||||
| 729 | /* Decrement link count if applicable. */ | ||||
| 730 | if (nlinks
| ||||
| 731 | p->fts_info == FTS_DC2 || p->fts_info == FTS_DOT5)) | ||||
| 732 | --nlinks; | ||||
| 733 | } | ||||
| 734 | |||||
| 735 | /* We walk in directory order so "ls -f" doesn't get upset. */ | ||||
| 736 | p->fts_link = NULL((void *)0); | ||||
| 737 | if (head
| ||||
| 738 | head = tail = p; | ||||
| 739 | else { | ||||
| 740 | tail->fts_link = p; | ||||
| 741 | tail = p; | ||||
| 742 | } | ||||
| 743 | ++nitems; | ||||
| 744 | } | ||||
| 745 | if (dirp
| ||||
| 746 | (void)closedir(dirp); | ||||
| 747 | |||||
| 748 | /* | ||||
| 749 | * If realloc() changed the address of the path, adjust the | ||||
| 750 | * addresses for the rest of the tree and the dir list. | ||||
| 751 | */ | ||||
| 752 | if (doadjust
| ||||
| 753 | fts_padjust(sp, head); | ||||
| 754 | |||||
| 755 | /* | ||||
| 756 | * If not changing directories, reset the path back to original | ||||
| 757 | * state. | ||||
| 758 | */ | ||||
| 759 | if (ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) { | ||||
| 760 | if (len == sp->fts_pathlen || nitems
| ||||
| 761 | --cp; | ||||
| 762 | *cp = '\0'; | ||||
| |||||
| 763 | } | ||||
| 764 | |||||
| 765 | /* | ||||
| 766 | * If descended after called from fts_children or after called from | ||||
| 767 | * fts_read and nothing found, get back. At the root level we use | ||||
| 768 | * the saved fd; if one of fts_open()'s arguments is a relative path | ||||
| 769 | * to an empty directory, we wind up here with no other way back. If | ||||
| 770 | * can't get back, we're done. | ||||
| 771 | */ | ||||
| 772 | if (descend && (type == BCHILD1 || !nitems) && | ||||
| 773 | (cur->fts_level == FTS_ROOTLEVEL0 ? FCHDIR(sp, sp->fts_rfd)(!(sp->fts_options & (0x0004)) && fchdir(sp-> fts_rfd)) : | ||||
| 774 | fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { | ||||
| 775 | cur->fts_info = FTS_ERR7; | ||||
| 776 | SET(FTS_STOP)(sp->fts_options |= (0x2000)); | ||||
| 777 | return (NULL((void *)0)); | ||||
| 778 | } | ||||
| 779 | |||||
| 780 | /* If didn't find anything, return NULL. */ | ||||
| 781 | if (!nitems) { | ||||
| 782 | if (type == BREAD3) | ||||
| 783 | cur->fts_info = FTS_DP6; | ||||
| 784 | return (NULL((void *)0)); | ||||
| 785 | } | ||||
| 786 | |||||
| 787 | /* Sort the entries. */ | ||||
| 788 | if (sp->fts_compar && nitems > 1) | ||||
| 789 | head = fts_sort(sp, head, nitems); | ||||
| 790 | return (head); | ||||
| 791 | } | ||||
| 792 | |||||
| 793 | static u_short | ||||
| 794 | fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) | ||||
| 795 | { | ||||
| 796 | FTSENT *t; | ||||
| 797 | dev_t dev; | ||||
| 798 | ino_t ino; | ||||
| 799 | struct stat *sbp, sb; | ||||
| 800 | int saved_errno; | ||||
| 801 | const char *path; | ||||
| 802 | |||||
| 803 | if (dfd == -1) { | ||||
| 804 | path = p->fts_accpath; | ||||
| 805 | dfd = AT_FDCWD-100; | ||||
| 806 | } else | ||||
| 807 | path = p->fts_name; | ||||
| 808 | |||||
| 809 | /* If user needs stat info, stat buffer already allocated. */ | ||||
| 810 | sbp = ISSET(FTS_NOSTAT)(sp->fts_options & (0x0008)) ? &sb : p->fts_statp; | ||||
| 811 | |||||
| 812 | /* | ||||
| 813 | * If doing a logical walk, or application requested FTS_FOLLOW, do | ||||
| 814 | * a stat(2). If that fails, check for a non-existent symlink. If | ||||
| 815 | * fail, set the errno from the stat call. | ||||
| 816 | */ | ||||
| 817 | if (ISSET(FTS_LOGICAL)(sp->fts_options & (0x0002)) || follow) { | ||||
| 818 | if (fstatat(dfd, path, sbp, 0)) { | ||||
| 819 | saved_errno = errno(*__errno()); | ||||
| 820 | if (!fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW0x02)) { | ||||
| 821 | errno(*__errno()) = 0; | ||||
| 822 | return (FTS_SLNONE13); | ||||
| 823 | } | ||||
| 824 | p->fts_errno = saved_errno; | ||||
| 825 | goto err; | ||||
| 826 | } | ||||
| 827 | } else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW0x02)) { | ||||
| 828 | p->fts_errno = errno(*__errno()); | ||||
| 829 | err: memset(sbp, 0, sizeof(struct stat)); | ||||
| 830 | return (FTS_NS10); | ||||
| 831 | } | ||||
| 832 | |||||
| 833 | if (S_ISDIR(sbp->st_mode)((sbp->st_mode & 0170000) == 0040000)) { | ||||
| 834 | /* | ||||
| 835 | * Set the device/inode. Used to find cycles and check for | ||||
| 836 | * crossing mount points. Also remember the link count, used | ||||
| 837 | * in fts_build to limit the number of stat calls. It is | ||||
| 838 | * understood that these fields are only referenced if fts_info | ||||
| 839 | * is set to FTS_D. | ||||
| 840 | */ | ||||
| 841 | dev = p->fts_dev = sbp->st_dev; | ||||
| 842 | ino = p->fts_ino = sbp->st_ino; | ||||
| 843 | p->fts_nlink = sbp->st_nlink; | ||||
| 844 | |||||
| 845 | if (ISDOT(p->fts_name)(p->fts_name[0] == '.' && (!p->fts_name[1] || ( p->fts_name[1] == '.' && !p->fts_name[2])))) | ||||
| 846 | return (FTS_DOT5); | ||||
| 847 | |||||
| 848 | /* | ||||
| 849 | * Cycle detection is done by brute force when the directory | ||||
| 850 | * is first encountered. If the tree gets deep enough or the | ||||
| 851 | * number of symbolic links to directories is high enough, | ||||
| 852 | * something faster might be worthwhile. | ||||
| 853 | */ | ||||
| 854 | for (t = p->fts_parent; | ||||
| 855 | t->fts_level >= FTS_ROOTLEVEL0; t = t->fts_parent) | ||||
| 856 | if (ino == t->fts_ino && dev == t->fts_dev) { | ||||
| 857 | p->fts_cycle = t; | ||||
| 858 | return (FTS_DC2); | ||||
| 859 | } | ||||
| 860 | return (FTS_D1); | ||||
| 861 | } | ||||
| 862 | if (S_ISLNK(sbp->st_mode)((sbp->st_mode & 0170000) == 0120000)) | ||||
| 863 | return (FTS_SL12); | ||||
| 864 | if (S_ISREG(sbp->st_mode)((sbp->st_mode & 0170000) == 0100000)) | ||||
| 865 | return (FTS_F8); | ||||
| 866 | return (FTS_DEFAULT3); | ||||
| 867 | } | ||||
| 868 | |||||
| 869 | static FTSENT * | ||||
| 870 | fts_sort(FTS *sp, FTSENT *head, int nitems) | ||||
| 871 | { | ||||
| 872 | FTSENT **ap, *p; | ||||
| 873 | |||||
| 874 | /* | ||||
| 875 | * Construct an array of pointers to the structures and call qsort(3). | ||||
| 876 | * Reassemble the array in the order returned by qsort. If unable to | ||||
| 877 | * sort for memory reasons, return the directory entries in their | ||||
| 878 | * current order. Allocate enough space for the current needs plus | ||||
| 879 | * 40 so don't realloc one entry at a time. | ||||
| 880 | */ | ||||
| 881 | if (nitems > sp->fts_nitems) { | ||||
| 882 | struct _ftsent **a; | ||||
| 883 | |||||
| 884 | if ((a = reallocarray(sp->fts_array, | ||||
| 885 | nitems + 40, sizeof(FTSENT *))) == NULL((void *)0)) { | ||||
| 886 | free(sp->fts_array); | ||||
| 887 | sp->fts_array = NULL((void *)0); | ||||
| 888 | sp->fts_nitems = 0; | ||||
| 889 | return (head); | ||||
| 890 | } | ||||
| 891 | sp->fts_nitems = nitems + 40; | ||||
| 892 | sp->fts_array = a; | ||||
| 893 | } | ||||
| 894 | for (ap = sp->fts_array, p = head; p; p = p->fts_link) | ||||
| 895 | *ap++ = p; | ||||
| 896 | qsort(sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); | ||||
| 897 | for (head = *(ap = sp->fts_array); --nitems; ++ap) | ||||
| 898 | ap[0]->fts_link = ap[1]; | ||||
| 899 | ap[0]->fts_link = NULL((void *)0); | ||||
| 900 | return (head); | ||||
| 901 | } | ||||
| 902 | |||||
| 903 | static FTSENT * | ||||
| 904 | fts_alloc(FTS *sp, const char *name, size_t namelen) | ||||
| 905 | { | ||||
| 906 | FTSENT *p; | ||||
| 907 | size_t len; | ||||
| 908 | |||||
| 909 | /* | ||||
| 910 | * The file name is a variable length array and no stat structure is | ||||
| 911 | * necessary if the user has set the nostat bit. Allocate the FTSENT | ||||
| 912 | * structure, the file name and the stat structure in one chunk, but | ||||
| 913 | * be careful that the stat structure is reasonably aligned. Since the | ||||
| 914 | * fts_name field is declared to be of size 1, the fts_name pointer is | ||||
| 915 | * namelen + 2 before the first possible address of the stat structure. | ||||
| 916 | */ | ||||
| 917 | len = sizeof(FTSENT) + namelen; | ||||
| 918 | if (!ISSET(FTS_NOSTAT)(sp->fts_options & (0x0008))) | ||||
| 919 | len += sizeof(struct stat) + ALIGNBYTES(sizeof(long) - 1); | ||||
| 920 | if ((p = calloc(1, len)) == NULL((void *)0)) | ||||
| 921 | return (NULL((void *)0)); | ||||
| 922 | |||||
| 923 | p->fts_path = sp->fts_path; | ||||
| 924 | p->fts_namelen = namelen; | ||||
| 925 | p->fts_instr = FTS_NOINSTR3; | ||||
| 926 | if (!ISSET(FTS_NOSTAT)(sp->fts_options & (0x0008))) | ||||
| 927 | p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2)(((unsigned long)(p->fts_name + namelen + 2) + (sizeof(long ) - 1)) &~(sizeof(long) - 1)); | ||||
| 928 | memcpy(p->fts_name, name, namelen); | ||||
| 929 | |||||
| 930 | return (p); | ||||
| 931 | } | ||||
| 932 | |||||
| 933 | static void | ||||
| 934 | fts_lfree(FTSENT *head) | ||||
| 935 | { | ||||
| 936 | FTSENT *p; | ||||
| 937 | |||||
| 938 | /* Free a linked list of structures. */ | ||||
| 939 | while ((p = head)) { | ||||
| 940 | head = head->fts_link; | ||||
| 941 | free(p); | ||||
| 942 | } | ||||
| 943 | } | ||||
| 944 | |||||
| 945 | /* | ||||
| 946 | * Allow essentially unlimited paths; find, rm, ls should all work on any tree. | ||||
| 947 | * Most systems will allow creation of paths much longer than PATH_MAX, even | ||||
| 948 | * though the kernel won't resolve them. Add the size (not just what's needed) | ||||
| 949 | * plus 256 bytes so don't realloc the path 2 bytes at a time. | ||||
| 950 | */ | ||||
| 951 | static int | ||||
| 952 | fts_palloc(FTS *sp, size_t more) | ||||
| 953 | { | ||||
| 954 | char *p; | ||||
| 955 | |||||
| 956 | /* | ||||
| 957 | * Check for possible wraparound. | ||||
| 958 | */ | ||||
| 959 | more += 256; | ||||
| 960 | if (sp->fts_pathlen + more < sp->fts_pathlen) { | ||||
| 961 | free(sp->fts_path); | ||||
| 962 | sp->fts_path = NULL((void *)0); | ||||
| 963 | errno(*__errno()) = ENAMETOOLONG63; | ||||
| 964 | return (1); | ||||
| 965 | } | ||||
| 966 | p = recallocarray(sp->fts_path, sp->fts_pathlen, | ||||
| 967 | sp->fts_pathlen + more, 1); | ||||
| 968 | if (p == NULL((void *)0)) { | ||||
| 969 | free(sp->fts_path); | ||||
| 970 | sp->fts_path = NULL((void *)0); | ||||
| 971 | return (1); | ||||
| 972 | } | ||||
| 973 | sp->fts_pathlen += more; | ||||
| 974 | sp->fts_path = p; | ||||
| 975 | return (0); | ||||
| 976 | } | ||||
| 977 | |||||
| 978 | /* | ||||
| 979 | * When the path is realloc'd, have to fix all of the pointers in structures | ||||
| 980 | * already returned. | ||||
| 981 | */ | ||||
| 982 | static void | ||||
| 983 | fts_padjust(FTS *sp, FTSENT *head) | ||||
| 984 | { | ||||
| 985 | FTSENT *p; | ||||
| 986 | char *addr = sp->fts_path; | ||||
| 987 | |||||
| 988 | #define ADJUST(p){ if ((p)->fts_accpath != (p)->fts_name) { (p)->fts_accpath = (char *)addr + ((p)->fts_accpath - (p)->fts_path); } (p)->fts_path = addr; } { \ | ||||
| 989 | if ((p)->fts_accpath != (p)->fts_name) { \ | ||||
| 990 | (p)->fts_accpath = \ | ||||
| 991 | (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ | ||||
| 992 | } \ | ||||
| 993 | (p)->fts_path = addr; \ | ||||
| 994 | } | ||||
| 995 | /* Adjust the current set of children. */ | ||||
| 996 | for (p = sp->fts_child; p; p = p->fts_link) | ||||
| 997 | ADJUST(p){ if ((p)->fts_accpath != (p)->fts_name) { (p)->fts_accpath = (char *)addr + ((p)->fts_accpath - (p)->fts_path); } (p)->fts_path = addr; }; | ||||
| 998 | |||||
| 999 | /* Adjust the rest of the tree, including the current level. */ | ||||
| 1000 | for (p = head; p->fts_level >= FTS_ROOTLEVEL0;) { | ||||
| 1001 | ADJUST(p){ if ((p)->fts_accpath != (p)->fts_name) { (p)->fts_accpath = (char *)addr + ((p)->fts_accpath - (p)->fts_path); } (p)->fts_path = addr; }; | ||||
| 1002 | p = p->fts_link ? p->fts_link : p->fts_parent; | ||||
| 1003 | } | ||||
| 1004 | } | ||||
| 1005 | |||||
| 1006 | static size_t | ||||
| 1007 | fts_maxarglen(char * const *argv) | ||||
| 1008 | { | ||||
| 1009 | size_t len, max; | ||||
| 1010 | |||||
| 1011 | for (max = 0; *argv; ++argv) | ||||
| 1012 | if ((len = strlen(*argv)) > max) | ||||
| 1013 | max = len; | ||||
| 1014 | return (max + 1); | ||||
| 1015 | } | ||||
| 1016 | |||||
| 1017 | /* | ||||
| 1018 | * Change to dir specified by fd or p->fts_accpath without getting | ||||
| 1019 | * tricked by someone changing the world out from underneath us. | ||||
| 1020 | * Assumes p->fts_dev and p->fts_ino are filled in. | ||||
| 1021 | */ | ||||
| 1022 | static int | ||||
| 1023 | fts_safe_changedir(FTS *sp, FTSENT *p, int fd, const char *path) | ||||
| 1024 | { | ||||
| 1025 | int ret, oerrno, newfd; | ||||
| 1026 | struct stat sb; | ||||
| 1027 | |||||
| 1028 | newfd = fd; | ||||
| 1029 | if (ISSET(FTS_NOCHDIR)(sp->fts_options & (0x0004))) | ||||
| 1030 | return (0); | ||||
| 1031 | if (fd == -1 && (newfd = open(path, O_RDONLY0x0000|O_DIRECTORY0x20000|O_CLOEXEC0x10000)) == -1) | ||||
| 1032 | return (-1); | ||||
| 1033 | if (fstat(newfd, &sb) == -1) { | ||||
| 1034 | ret = -1; | ||||
| 1035 | goto bail; | ||||
| 1036 | } | ||||
| 1037 | if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) { | ||||
| 1038 | errno(*__errno()) = ENOENT2; /* disinformation */ | ||||
| 1039 | ret = -1; | ||||
| 1040 | goto bail; | ||||
| 1041 | } | ||||
| 1042 | ret = fchdir(newfd); | ||||
| 1043 | bail: | ||||
| 1044 | oerrno = errno(*__errno()); | ||||
| 1045 | if (fd == -1) | ||||
| 1046 | (void)close(newfd); | ||||
| 1047 | errno(*__errno()) = oerrno; | ||||
| 1048 | return (ret); | ||||
| 1049 | } |