| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* File tree walker functions. | 
|  | 2 | Copyright (C) 1996-2016 Free Software Foundation, Inc. | 
|  | 3 | This file is part of the GNU C Library. | 
|  | 4 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. | 
|  | 5 |  | 
|  | 6 | The GNU C Library is free software; you can redistribute it and/or | 
|  | 7 | modify it under the terms of the GNU Lesser General Public | 
|  | 8 | License as published by the Free Software Foundation; either | 
|  | 9 | version 2.1 of the License, or (at your option) any later version. | 
|  | 10 |  | 
|  | 11 | The GNU C Library is distributed in the hope that it will be useful, | 
|  | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | 14 | Lesser General Public License for more details. | 
|  | 15 |  | 
|  | 16 | You should have received a copy of the GNU Lesser General Public | 
|  | 17 | License along with the GNU C Library; if not, see | 
|  | 18 | <http://www.gnu.org/licenses/>.  */ | 
|  | 19 |  | 
|  | 20 | #ifdef HAVE_CONFIG_H | 
|  | 21 | # include <config.h> | 
|  | 22 | #endif | 
|  | 23 |  | 
|  | 24 | #if __GNUC__ | 
|  | 25 | # define alloca __builtin_alloca | 
|  | 26 | #else | 
|  | 27 | # if HAVE_ALLOCA_H | 
|  | 28 | #  include <alloca.h> | 
|  | 29 | # else | 
|  | 30 | #  ifdef _AIX | 
|  | 31 | #  pragma alloca | 
|  | 32 | #  else | 
|  | 33 | char *alloca (); | 
|  | 34 | #  endif | 
|  | 35 | # endif | 
|  | 36 | #endif | 
|  | 37 |  | 
|  | 38 | #ifdef _LIBC | 
|  | 39 | # include <dirent.h> | 
|  | 40 | # define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent) | 
|  | 41 | #else | 
|  | 42 | # if HAVE_DIRENT_H | 
|  | 43 | #  include <dirent.h> | 
|  | 44 | #  define NAMLEN(dirent) strlen ((dirent)->d_name) | 
|  | 45 | # else | 
|  | 46 | #  define dirent direct | 
|  | 47 | #  define NAMLEN(dirent) (dirent)->d_namlen | 
|  | 48 | #  if HAVE_SYS_NDIR_H | 
|  | 49 | #   include <sys/ndir.h> | 
|  | 50 | #  endif | 
|  | 51 | #  if HAVE_SYS_DIR_H | 
|  | 52 | #   include <sys/dir.h> | 
|  | 53 | #  endif | 
|  | 54 | #  if HAVE_NDIR_H | 
|  | 55 | #   include <ndir.h> | 
|  | 56 | #  endif | 
|  | 57 | # endif | 
|  | 58 | #endif | 
|  | 59 |  | 
|  | 60 | #include <errno.h> | 
|  | 61 | #include <fcntl.h> | 
|  | 62 | #include <ftw.h> | 
|  | 63 | #include <limits.h> | 
|  | 64 | #include <search.h> | 
|  | 65 | #include <stdlib.h> | 
|  | 66 | #include <string.h> | 
|  | 67 | #include <unistd.h> | 
|  | 68 | #include <not-cancel.h> | 
|  | 69 | #include <sys/param.h> | 
|  | 70 | #ifdef _LIBC | 
|  | 71 | # include <include/sys/stat.h> | 
|  | 72 | #else | 
|  | 73 | # include <sys/stat.h> | 
|  | 74 | #endif | 
|  | 75 |  | 
|  | 76 | #if ! _LIBC && !HAVE_DECL_STPCPY && !defined stpcpy | 
|  | 77 | char *stpcpy (); | 
|  | 78 | #endif | 
|  | 79 |  | 
|  | 80 | #if ! _LIBC && ! defined HAVE_MEMPCPY && ! defined mempcpy | 
|  | 81 | /* Be CAREFUL that there are no side effects in N.  */ | 
|  | 82 | # define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N))) | 
|  | 83 | #endif | 
|  | 84 |  | 
|  | 85 | /* #define NDEBUG 1 */ | 
|  | 86 | #include <assert.h> | 
|  | 87 |  | 
|  | 88 | #ifndef _LIBC | 
|  | 89 | # undef __chdir | 
|  | 90 | # define __chdir chdir | 
|  | 91 | # undef __closedir | 
|  | 92 | # define __closedir closedir | 
|  | 93 | # undef __fchdir | 
|  | 94 | # define __fchdir fchdir | 
|  | 95 | # undef __getcwd | 
|  | 96 | # define __getcwd(P, N) xgetcwd () | 
|  | 97 | extern char *xgetcwd (void); | 
|  | 98 | # undef __mempcpy | 
|  | 99 | # define __mempcpy mempcpy | 
|  | 100 | # undef __opendir | 
|  | 101 | # define __opendir opendir | 
|  | 102 | # undef __readdir64 | 
|  | 103 | # define __readdir64 readdir | 
|  | 104 | # undef __stpcpy | 
|  | 105 | # define __stpcpy stpcpy | 
|  | 106 | # undef __tdestroy | 
|  | 107 | # define __tdestroy tdestroy | 
|  | 108 | # undef __tfind | 
|  | 109 | # define __tfind tfind | 
|  | 110 | # undef __tsearch | 
|  | 111 | # define __tsearch tsearch | 
|  | 112 | # undef internal_function | 
|  | 113 | # define internal_function /* empty */ | 
|  | 114 | # undef dirent64 | 
|  | 115 | # define dirent64 dirent | 
|  | 116 | # undef MAX | 
|  | 117 | # define MAX(a, b) ((a) > (b) ? (a) : (b)) | 
|  | 118 | #endif | 
|  | 119 |  | 
|  | 120 | /* Arrange to make lstat calls go through the wrapper function | 
|  | 121 | on systems with an lstat function that does not dereference symlinks | 
|  | 122 | that are specified with a trailing slash.  */ | 
|  | 123 | #if ! _LIBC && ! LSTAT_FOLLOWS_SLASHED_SYMLINK | 
|  | 124 | int rpl_lstat (const char *, struct stat *); | 
|  | 125 | # undef lstat | 
|  | 126 | # define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf) | 
|  | 127 | #endif | 
|  | 128 |  | 
|  | 129 | #ifndef __set_errno | 
|  | 130 | # define __set_errno(Val) errno = (Val) | 
|  | 131 | #endif | 
|  | 132 |  | 
|  | 133 | /* Support for the LFS API version.  */ | 
|  | 134 | #ifndef FTW_NAME | 
|  | 135 | # define FTW_NAME ftw | 
|  | 136 | # define NFTW_NAME nftw | 
|  | 137 | # define NFTW_OLD_NAME __old_nftw | 
|  | 138 | # define NFTW_NEW_NAME __new_nftw | 
|  | 139 | # define INO_T ino_t | 
|  | 140 | # define STAT stat | 
|  | 141 | # ifdef _LIBC | 
|  | 142 | #  define LXSTAT __lxstat | 
|  | 143 | #  define XSTAT __xstat | 
|  | 144 | #  define FXSTATAT __fxstatat | 
|  | 145 | # else | 
|  | 146 | #  define LXSTAT(V,f,sb) lstat (f,sb) | 
|  | 147 | #  define XSTAT(V,f,sb) stat (f,sb) | 
|  | 148 | #  define FXSTATAT(V,d,f,sb,m) fstatat (d, f, sb, m) | 
|  | 149 | # endif | 
|  | 150 | # define FTW_FUNC_T __ftw_func_t | 
|  | 151 | # define NFTW_FUNC_T __nftw_func_t | 
|  | 152 | #endif | 
|  | 153 |  | 
|  | 154 | /* We define PATH_MAX if the system does not provide a definition. | 
|  | 155 | This does not artificially limit any operation.  PATH_MAX is simply | 
|  | 156 | used as a guesstimate for the expected maximal path length. | 
|  | 157 | Buffers will be enlarged if necessary.  */ | 
|  | 158 | #ifndef PATH_MAX | 
|  | 159 | # define PATH_MAX 1024 | 
|  | 160 | #endif | 
|  | 161 |  | 
|  | 162 | struct dir_data | 
|  | 163 | { | 
|  | 164 | DIR *stream; | 
|  | 165 | int streamfd; | 
|  | 166 | char *content; | 
|  | 167 | }; | 
|  | 168 |  | 
|  | 169 | struct known_object | 
|  | 170 | { | 
|  | 171 | dev_t dev; | 
|  | 172 | INO_T ino; | 
|  | 173 | }; | 
|  | 174 |  | 
|  | 175 | struct ftw_data | 
|  | 176 | { | 
|  | 177 | /* Array with pointers to open directory streams.  */ | 
|  | 178 | struct dir_data **dirstreams; | 
|  | 179 | size_t actdir; | 
|  | 180 | size_t maxdir; | 
|  | 181 |  | 
|  | 182 | /* Buffer containing name of currently processed object.  */ | 
|  | 183 | char *dirbuf; | 
|  | 184 | size_t dirbufsize; | 
|  | 185 |  | 
|  | 186 | /* Passed as fourth argument to `nftw' callback.  The `base' member | 
|  | 187 | tracks the content of the `dirbuf'.  */ | 
|  | 188 | struct FTW ftw; | 
|  | 189 |  | 
|  | 190 | /* Flags passed to `nftw' function.  0 for `ftw'.  */ | 
|  | 191 | int flags; | 
|  | 192 |  | 
|  | 193 | /* Conversion array for flag values.  It is the identity mapping for | 
|  | 194 | `nftw' calls, otherwise it maps the values to those known by | 
|  | 195 | `ftw'.  */ | 
|  | 196 | const int *cvt_arr; | 
|  | 197 |  | 
|  | 198 | /* Callback function.  We always use the `nftw' form.  */ | 
|  | 199 | NFTW_FUNC_T func; | 
|  | 200 |  | 
|  | 201 | /* Device of starting point.  Needed for FTW_MOUNT.  */ | 
|  | 202 | dev_t dev; | 
|  | 203 |  | 
|  | 204 | /* Data structure for keeping fingerprints of already processed | 
|  | 205 | object.  This is needed when not using FTW_PHYS.  */ | 
|  | 206 | void *known_objects; | 
|  | 207 | }; | 
|  | 208 |  | 
|  | 209 |  | 
|  | 210 | /* Internally we use the FTW_* constants used for `nftw'.  When invoked | 
|  | 211 | as `ftw', map each flag to the subset of values used by `ftw'.  */ | 
|  | 212 | static const int nftw_arr[] = | 
|  | 213 | { | 
|  | 214 | FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN | 
|  | 215 | }; | 
|  | 216 |  | 
|  | 217 | static const int ftw_arr[] = | 
|  | 218 | { | 
|  | 219 | FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS | 
|  | 220 | }; | 
|  | 221 |  | 
|  | 222 |  | 
|  | 223 | /* Forward declarations of local functions.  */ | 
|  | 224 | static int ftw_dir (struct ftw_data *data, struct STAT *st, | 
|  | 225 | struct dir_data *old_dir) internal_function; | 
|  | 226 |  | 
|  | 227 |  | 
|  | 228 | static int | 
|  | 229 | object_compare (const void *p1, const void *p2) | 
|  | 230 | { | 
|  | 231 | /* We don't need a sophisticated and useful comparison.  We are only | 
|  | 232 | interested in equality.  However, we must be careful not to | 
|  | 233 | accidentally compare `holes' in the structure.  */ | 
|  | 234 | const struct known_object *kp1 = p1, *kp2 = p2; | 
|  | 235 | int cmp1; | 
|  | 236 | cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino); | 
|  | 237 | if (cmp1 != 0) | 
|  | 238 | return cmp1; | 
|  | 239 | return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev); | 
|  | 240 | } | 
|  | 241 |  | 
|  | 242 |  | 
|  | 243 | static int | 
|  | 244 | add_object (struct ftw_data *data, struct STAT *st) | 
|  | 245 | { | 
|  | 246 | struct known_object *newp = malloc (sizeof (struct known_object)); | 
|  | 247 | if (newp == NULL) | 
|  | 248 | return -1; | 
|  | 249 | newp->dev = st->st_dev; | 
|  | 250 | newp->ino = st->st_ino; | 
|  | 251 | return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1; | 
|  | 252 | } | 
|  | 253 |  | 
|  | 254 |  | 
|  | 255 | static inline int | 
|  | 256 | find_object (struct ftw_data *data, struct STAT *st) | 
|  | 257 | { | 
|  | 258 | struct known_object obj; | 
|  | 259 | obj.dev = st->st_dev; | 
|  | 260 | obj.ino = st->st_ino; | 
|  | 261 | return __tfind (&obj, &data->known_objects, object_compare) != NULL; | 
|  | 262 | } | 
|  | 263 |  | 
|  | 264 |  | 
|  | 265 | static inline int | 
|  | 266 | __attribute ((always_inline)) | 
|  | 267 | open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp) | 
|  | 268 | { | 
|  | 269 | int result = 0; | 
|  | 270 |  | 
|  | 271 | if (data->dirstreams[data->actdir] != NULL) | 
|  | 272 | { | 
|  | 273 | /* Oh, oh.  We must close this stream.  Get all remaining | 
|  | 274 | entries and store them as a list in the `content' member of | 
|  | 275 | the `struct dir_data' variable.  */ | 
|  | 276 | size_t bufsize = 1024; | 
|  | 277 | char *buf = malloc (bufsize); | 
|  | 278 |  | 
|  | 279 | if (buf == NULL) | 
|  | 280 | result = -1; | 
|  | 281 | else | 
|  | 282 | { | 
|  | 283 | DIR *st = data->dirstreams[data->actdir]->stream; | 
|  | 284 | struct dirent64 *d; | 
|  | 285 | size_t actsize = 0; | 
|  | 286 |  | 
|  | 287 | while ((d = __readdir64 (st)) != NULL) | 
|  | 288 | { | 
|  | 289 | size_t this_len = NAMLEN (d); | 
|  | 290 | if (actsize + this_len + 2 >= bufsize) | 
|  | 291 | { | 
|  | 292 | char *newp; | 
|  | 293 | bufsize += MAX (1024, 2 * this_len); | 
|  | 294 | newp = (char *) realloc (buf, bufsize); | 
|  | 295 | if (newp == NULL) | 
|  | 296 | { | 
|  | 297 | /* No more memory.  */ | 
|  | 298 | int save_err = errno; | 
|  | 299 | free (buf); | 
|  | 300 | __set_errno (save_err); | 
|  | 301 | return -1; | 
|  | 302 | } | 
|  | 303 | buf = newp; | 
|  | 304 | } | 
|  | 305 |  | 
|  | 306 | *((char *) __mempcpy (buf + actsize, d->d_name, this_len)) | 
|  | 307 | = '\0'; | 
|  | 308 | actsize += this_len + 1; | 
|  | 309 | } | 
|  | 310 |  | 
|  | 311 | /* Terminate the list with an additional NUL byte.  */ | 
|  | 312 | buf[actsize++] = '\0'; | 
|  | 313 |  | 
|  | 314 | /* Shrink the buffer to what we actually need.  */ | 
|  | 315 | data->dirstreams[data->actdir]->content = realloc (buf, actsize); | 
|  | 316 | if (data->dirstreams[data->actdir]->content == NULL) | 
|  | 317 | { | 
|  | 318 | int save_err = errno; | 
|  | 319 | free (buf); | 
|  | 320 | __set_errno (save_err); | 
|  | 321 | result = -1; | 
|  | 322 | } | 
|  | 323 | else | 
|  | 324 | { | 
|  | 325 | __closedir (st); | 
|  | 326 | data->dirstreams[data->actdir]->stream = NULL; | 
|  | 327 | data->dirstreams[data->actdir]->streamfd = -1; | 
|  | 328 | data->dirstreams[data->actdir] = NULL; | 
|  | 329 | } | 
|  | 330 | } | 
|  | 331 | } | 
|  | 332 |  | 
|  | 333 | /* Open the new stream.  */ | 
|  | 334 | if (result == 0) | 
|  | 335 | { | 
|  | 336 | assert (data->dirstreams[data->actdir] == NULL); | 
|  | 337 |  | 
|  | 338 | if (dfdp != NULL && *dfdp != -1) | 
|  | 339 | { | 
|  | 340 | int fd = openat64_not_cancel_3 (*dfdp, data->dirbuf + data->ftw.base, | 
|  | 341 | O_RDONLY | O_DIRECTORY | O_NDELAY); | 
|  | 342 | dirp->stream = NULL; | 
|  | 343 | if (fd != -1 && (dirp->stream = __fdopendir (fd)) == NULL) | 
|  | 344 | close_not_cancel_no_status (fd); | 
|  | 345 | } | 
|  | 346 | else | 
|  | 347 | { | 
|  | 348 | const char *name; | 
|  | 349 |  | 
|  | 350 | if (data->flags & FTW_CHDIR) | 
|  | 351 | { | 
|  | 352 | name = data->dirbuf + data->ftw.base; | 
|  | 353 | if (name[0] == '\0') | 
|  | 354 | name = "."; | 
|  | 355 | } | 
|  | 356 | else | 
|  | 357 | name = data->dirbuf; | 
|  | 358 |  | 
|  | 359 | dirp->stream = __opendir (name); | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | if (dirp->stream == NULL) | 
|  | 363 | result = -1; | 
|  | 364 | else | 
|  | 365 | { | 
|  | 366 | dirp->streamfd = dirfd (dirp->stream); | 
|  | 367 | dirp->content = NULL; | 
|  | 368 | data->dirstreams[data->actdir] = dirp; | 
|  | 369 |  | 
|  | 370 | if (++data->actdir == data->maxdir) | 
|  | 371 | data->actdir = 0; | 
|  | 372 | } | 
|  | 373 | } | 
|  | 374 |  | 
|  | 375 | return result; | 
|  | 376 | } | 
|  | 377 |  | 
|  | 378 |  | 
|  | 379 | static int | 
|  | 380 | internal_function | 
|  | 381 | process_entry (struct ftw_data *data, struct dir_data *dir, const char *name, | 
|  | 382 | size_t namlen, int d_type) | 
|  | 383 | { | 
|  | 384 | struct STAT st; | 
|  | 385 | int result = 0; | 
|  | 386 | int flag = 0; | 
|  | 387 | size_t new_buflen; | 
|  | 388 |  | 
|  | 389 | if (name[0] == '.' && (name[1] == '\0' | 
|  | 390 | || (name[1] == '.' && name[2] == '\0'))) | 
|  | 391 | /* Don't process the "." and ".." entries.  */ | 
|  | 392 | return 0; | 
|  | 393 |  | 
|  | 394 | new_buflen = data->ftw.base + namlen + 2; | 
|  | 395 | if (data->dirbufsize < new_buflen) | 
|  | 396 | { | 
|  | 397 | /* Enlarge the buffer.  */ | 
|  | 398 | char *newp; | 
|  | 399 |  | 
|  | 400 | data->dirbufsize = 2 * new_buflen; | 
|  | 401 | newp = (char *) realloc (data->dirbuf, data->dirbufsize); | 
|  | 402 | if (newp == NULL) | 
|  | 403 | return -1; | 
|  | 404 | data->dirbuf = newp; | 
|  | 405 | } | 
|  | 406 |  | 
|  | 407 | *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0'; | 
|  | 408 |  | 
|  | 409 | int statres; | 
|  | 410 | if (dir->streamfd != -1) | 
|  | 411 | statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st, | 
|  | 412 | (data->flags & FTW_PHYS) ? AT_SYMLINK_NOFOLLOW : 0); | 
|  | 413 | else | 
|  | 414 | { | 
|  | 415 | if ((data->flags & FTW_CHDIR) == 0) | 
|  | 416 | name = data->dirbuf; | 
|  | 417 |  | 
|  | 418 | statres = ((data->flags & FTW_PHYS) | 
|  | 419 | ? LXSTAT (_STAT_VER, name, &st) | 
|  | 420 | : XSTAT (_STAT_VER, name, &st)); | 
|  | 421 | } | 
|  | 422 |  | 
|  | 423 | if (statres < 0) | 
|  | 424 | { | 
|  | 425 | if (errno != EACCES && errno != ENOENT) | 
|  | 426 | result = -1; | 
|  | 427 | else if (data->flags & FTW_PHYS) | 
|  | 428 | flag = FTW_NS; | 
|  | 429 | else if (d_type == DT_LNK) | 
|  | 430 | flag = FTW_SLN; | 
|  | 431 | else | 
|  | 432 | { | 
|  | 433 | if (dir->streamfd != -1) | 
|  | 434 | statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st, | 
|  | 435 | AT_SYMLINK_NOFOLLOW); | 
|  | 436 | else | 
|  | 437 | statres = LXSTAT (_STAT_VER, name, &st); | 
|  | 438 | if (statres == 0 && S_ISLNK (st.st_mode)) | 
|  | 439 | flag = FTW_SLN; | 
|  | 440 | else | 
|  | 441 | flag = FTW_NS; | 
|  | 442 | } | 
|  | 443 | } | 
|  | 444 | else | 
|  | 445 | { | 
|  | 446 | if (S_ISDIR (st.st_mode)) | 
|  | 447 | flag = FTW_D; | 
|  | 448 | else if (S_ISLNK (st.st_mode)) | 
|  | 449 | flag = FTW_SL; | 
|  | 450 | else | 
|  | 451 | flag = FTW_F; | 
|  | 452 | } | 
|  | 453 |  | 
|  | 454 | if (result == 0 | 
|  | 455 | && (flag == FTW_NS | 
|  | 456 | || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev)) | 
|  | 457 | { | 
|  | 458 | if (flag == FTW_D) | 
|  | 459 | { | 
|  | 460 | if ((data->flags & FTW_PHYS) | 
|  | 461 | || (!find_object (data, &st) | 
|  | 462 | /* Remember the object.  */ | 
|  | 463 | && (result = add_object (data, &st)) == 0)) | 
|  | 464 | result = ftw_dir (data, &st, dir); | 
|  | 465 | } | 
|  | 466 | else | 
|  | 467 | result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag], | 
|  | 468 | &data->ftw); | 
|  | 469 | } | 
|  | 470 |  | 
|  | 471 | if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE) | 
|  | 472 | result = 0; | 
|  | 473 |  | 
|  | 474 | return result; | 
|  | 475 | } | 
|  | 476 |  | 
|  | 477 |  | 
|  | 478 | static int | 
|  | 479 | __attribute ((noinline)) | 
|  | 480 | internal_function | 
|  | 481 | ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir) | 
|  | 482 | { | 
|  | 483 | struct dir_data dir; | 
|  | 484 | struct dirent64 *d; | 
|  | 485 | int previous_base = data->ftw.base; | 
|  | 486 | int result; | 
|  | 487 | char *startp; | 
|  | 488 |  | 
|  | 489 | /* Open the stream for this directory.  This might require that | 
|  | 490 | another stream has to be closed.  */ | 
|  | 491 | result = open_dir_stream (old_dir == NULL ? NULL : &old_dir->streamfd, | 
|  | 492 | data, &dir); | 
|  | 493 | if (result != 0) | 
|  | 494 | { | 
|  | 495 | if (errno == EACCES) | 
|  | 496 | /* We cannot read the directory.  Signal this with a special flag.  */ | 
|  | 497 | result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw); | 
|  | 498 |  | 
|  | 499 | return result; | 
|  | 500 | } | 
|  | 501 |  | 
|  | 502 | /* First, report the directory (if not depth-first).  */ | 
|  | 503 | if (!(data->flags & FTW_DEPTH)) | 
|  | 504 | { | 
|  | 505 | result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw); | 
|  | 506 | if (result != 0) | 
|  | 507 | { | 
|  | 508 | int save_err; | 
|  | 509 | fail: | 
|  | 510 | save_err = errno; | 
|  | 511 | __closedir (dir.stream); | 
|  | 512 | dir.streamfd = -1; | 
|  | 513 | __set_errno (save_err); | 
|  | 514 |  | 
|  | 515 | if (data->actdir-- == 0) | 
|  | 516 | data->actdir = data->maxdir - 1; | 
|  | 517 | data->dirstreams[data->actdir] = NULL; | 
|  | 518 | return result; | 
|  | 519 | } | 
|  | 520 | } | 
|  | 521 |  | 
|  | 522 | /* If necessary, change to this directory.  */ | 
|  | 523 | if (data->flags & FTW_CHDIR) | 
|  | 524 | { | 
|  | 525 | if (__fchdir (dirfd (dir.stream)) < 0) | 
|  | 526 | { | 
|  | 527 | result = -1; | 
|  | 528 | goto fail; | 
|  | 529 | } | 
|  | 530 | } | 
|  | 531 |  | 
|  | 532 | /* Next, update the `struct FTW' information.  */ | 
|  | 533 | ++data->ftw.level; | 
|  | 534 | startp = __rawmemchr (data->dirbuf, '\0'); | 
|  | 535 | /* There always must be a directory name.  */ | 
|  | 536 | assert (startp != data->dirbuf); | 
|  | 537 | if (startp[-1] != '/') | 
|  | 538 | *startp++ = '/'; | 
|  | 539 | data->ftw.base = startp - data->dirbuf; | 
|  | 540 |  | 
|  | 541 | while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL) | 
|  | 542 | { | 
|  | 543 | int d_type = DT_UNKNOWN; | 
|  | 544 | #ifdef _DIRENT_HAVE_D_TYPE | 
|  | 545 | d_type = d->d_type; | 
|  | 546 | #endif | 
|  | 547 | result = process_entry (data, &dir, d->d_name, NAMLEN (d), d_type); | 
|  | 548 | if (result != 0) | 
|  | 549 | break; | 
|  | 550 | } | 
|  | 551 |  | 
|  | 552 | if (dir.stream != NULL) | 
|  | 553 | { | 
|  | 554 | /* The stream is still open.  I.e., we did not need more | 
|  | 555 | descriptors.  Simply close the stream now.  */ | 
|  | 556 | int save_err = errno; | 
|  | 557 |  | 
|  | 558 | assert (dir.content == NULL); | 
|  | 559 |  | 
|  | 560 | __closedir (dir.stream); | 
|  | 561 | dir.streamfd = -1; | 
|  | 562 | __set_errno (save_err); | 
|  | 563 |  | 
|  | 564 | if (data->actdir-- == 0) | 
|  | 565 | data->actdir = data->maxdir - 1; | 
|  | 566 | data->dirstreams[data->actdir] = NULL; | 
|  | 567 | } | 
|  | 568 | else | 
|  | 569 | { | 
|  | 570 | int save_err; | 
|  | 571 | char *runp = dir.content; | 
|  | 572 |  | 
|  | 573 | while (result == 0 && *runp != '\0') | 
|  | 574 | { | 
|  | 575 | char *endp = strchr (runp, '\0'); | 
|  | 576 |  | 
|  | 577 | // XXX Should store the d_type values as well?! | 
|  | 578 | result = process_entry (data, &dir, runp, endp - runp, DT_UNKNOWN); | 
|  | 579 |  | 
|  | 580 | runp = endp + 1; | 
|  | 581 | } | 
|  | 582 |  | 
|  | 583 | save_err = errno; | 
|  | 584 | free (dir.content); | 
|  | 585 | __set_errno (save_err); | 
|  | 586 | } | 
|  | 587 |  | 
|  | 588 | if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS) | 
|  | 589 | result = 0; | 
|  | 590 |  | 
|  | 591 | /* Prepare the return, revert the `struct FTW' information.  */ | 
|  | 592 | data->dirbuf[data->ftw.base - 1] = '\0'; | 
|  | 593 | --data->ftw.level; | 
|  | 594 | data->ftw.base = previous_base; | 
|  | 595 |  | 
|  | 596 | /* Finally, if we process depth-first report the directory.  */ | 
|  | 597 | if (result == 0 && (data->flags & FTW_DEPTH)) | 
|  | 598 | result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw); | 
|  | 599 |  | 
|  | 600 | if (old_dir | 
|  | 601 | && (data->flags & FTW_CHDIR) | 
|  | 602 | && (result == 0 | 
|  | 603 | || ((data->flags & FTW_ACTIONRETVAL) | 
|  | 604 | && (result != -1 && result != FTW_STOP)))) | 
|  | 605 | { | 
|  | 606 | /* Change back to the parent directory.  */ | 
|  | 607 | int done = 0; | 
|  | 608 | if (old_dir->stream != NULL) | 
|  | 609 | if (__fchdir (dirfd (old_dir->stream)) == 0) | 
|  | 610 | done = 1; | 
|  | 611 |  | 
|  | 612 | if (!done) | 
|  | 613 | { | 
|  | 614 | if (data->ftw.base == 1) | 
|  | 615 | { | 
|  | 616 | if (__chdir ("/") < 0) | 
|  | 617 | result = -1; | 
|  | 618 | } | 
|  | 619 | else | 
|  | 620 | if (__chdir ("..") < 0) | 
|  | 621 | result = -1; | 
|  | 622 | } | 
|  | 623 | } | 
|  | 624 |  | 
|  | 625 | return result; | 
|  | 626 | } | 
|  | 627 |  | 
|  | 628 |  | 
|  | 629 | static int | 
|  | 630 | __attribute ((noinline)) | 
|  | 631 | internal_function | 
|  | 632 | ftw_startup (const char *dir, int is_nftw, void *func, int descriptors, | 
|  | 633 | int flags) | 
|  | 634 | { | 
|  | 635 | struct ftw_data data; | 
|  | 636 | struct STAT st; | 
|  | 637 | int result = 0; | 
|  | 638 | int save_err; | 
|  | 639 | int cwdfd = -1; | 
|  | 640 | char *cwd = NULL; | 
|  | 641 | char *cp; | 
|  | 642 |  | 
|  | 643 | /* First make sure the parameters are reasonable.  */ | 
|  | 644 | if (dir[0] == '\0') | 
|  | 645 | { | 
|  | 646 | __set_errno (ENOENT); | 
|  | 647 | return -1; | 
|  | 648 | } | 
|  | 649 |  | 
|  | 650 | data.maxdir = descriptors < 1 ? 1 : descriptors; | 
|  | 651 | data.actdir = 0; | 
|  | 652 | data.dirstreams = (struct dir_data **) alloca (data.maxdir | 
|  | 653 | * sizeof (struct dir_data *)); | 
|  | 654 | memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *)); | 
|  | 655 |  | 
|  | 656 | /* PATH_MAX is always defined when we get here.  */ | 
|  | 657 | data.dirbufsize = MAX (2 * strlen (dir), PATH_MAX); | 
|  | 658 | data.dirbuf = (char *) malloc (data.dirbufsize); | 
|  | 659 | if (data.dirbuf == NULL) | 
|  | 660 | return -1; | 
|  | 661 | cp = __stpcpy (data.dirbuf, dir); | 
|  | 662 | /* Strip trailing slashes.  */ | 
|  | 663 | while (cp > data.dirbuf + 1 && cp[-1] == '/') | 
|  | 664 | --cp; | 
|  | 665 | *cp = '\0'; | 
|  | 666 |  | 
|  | 667 | data.ftw.level = 0; | 
|  | 668 |  | 
|  | 669 | /* Find basename.  */ | 
|  | 670 | while (cp > data.dirbuf && cp[-1] != '/') | 
|  | 671 | --cp; | 
|  | 672 | data.ftw.base = cp - data.dirbuf; | 
|  | 673 |  | 
|  | 674 | data.flags = flags; | 
|  | 675 |  | 
|  | 676 | /* This assignment might seem to be strange but it is what we want. | 
|  | 677 | The trick is that the first three arguments to the `ftw' and | 
|  | 678 | `nftw' callback functions are equal.  Therefore we can call in | 
|  | 679 | every case the callback using the format of the `nftw' version | 
|  | 680 | and get the correct result since the stack layout for a function | 
|  | 681 | call in C allows this.  */ | 
|  | 682 | data.func = (NFTW_FUNC_T) func; | 
|  | 683 |  | 
|  | 684 | /* Since we internally use the complete set of FTW_* values we need | 
|  | 685 | to reduce the value range before calling a `ftw' callback.  */ | 
|  | 686 | data.cvt_arr = is_nftw ? nftw_arr : ftw_arr; | 
|  | 687 |  | 
|  | 688 | /* No object known so far.  */ | 
|  | 689 | data.known_objects = NULL; | 
|  | 690 |  | 
|  | 691 | /* Now go to the directory containing the initial file/directory.  */ | 
|  | 692 | if (flags & FTW_CHDIR) | 
|  | 693 | { | 
|  | 694 | /* We have to be able to go back to the current working | 
|  | 695 | directory.  The best way to do this is to use a file | 
|  | 696 | descriptor.  */ | 
|  | 697 | cwdfd = __open (".", O_RDONLY | O_DIRECTORY); | 
|  | 698 | if (cwdfd == -1) | 
|  | 699 | { | 
|  | 700 | /* Try getting the directory name.  This can be needed if | 
|  | 701 | the current directory is executable but not readable.  */ | 
|  | 702 | if (errno == EACCES) | 
|  | 703 | /* GNU extension ahead.  */ | 
|  | 704 | cwd =  __getcwd (NULL, 0); | 
|  | 705 |  | 
|  | 706 | if (cwd == NULL) | 
|  | 707 | goto out_fail; | 
|  | 708 | } | 
|  | 709 | else if (data.maxdir > 1) | 
|  | 710 | /* Account for the file descriptor we use here.  */ | 
|  | 711 | --data.maxdir; | 
|  | 712 |  | 
|  | 713 | if (data.ftw.base > 0) | 
|  | 714 | { | 
|  | 715 | /* Change to the directory the file is in.  In data.dirbuf | 
|  | 716 | we have a writable copy of the file name.  Just NUL | 
|  | 717 | terminate it for now and change the directory.  */ | 
|  | 718 | if (data.ftw.base == 1) | 
|  | 719 | /* I.e., the file is in the root directory.  */ | 
|  | 720 | result = __chdir ("/"); | 
|  | 721 | else | 
|  | 722 | { | 
|  | 723 | char ch = data.dirbuf[data.ftw.base - 1]; | 
|  | 724 | data.dirbuf[data.ftw.base - 1] = '\0'; | 
|  | 725 | result = __chdir (data.dirbuf); | 
|  | 726 | data.dirbuf[data.ftw.base - 1] = ch; | 
|  | 727 | } | 
|  | 728 | } | 
|  | 729 | } | 
|  | 730 |  | 
|  | 731 | /* Get stat info for start directory.  */ | 
|  | 732 | if (result == 0) | 
|  | 733 | { | 
|  | 734 | const char *name; | 
|  | 735 |  | 
|  | 736 | if (data.flags & FTW_CHDIR) | 
|  | 737 | { | 
|  | 738 | name = data.dirbuf + data.ftw.base; | 
|  | 739 | if (name[0] == '\0') | 
|  | 740 | name = "."; | 
|  | 741 | } | 
|  | 742 | else | 
|  | 743 | name = data.dirbuf; | 
|  | 744 |  | 
|  | 745 | if (((flags & FTW_PHYS) | 
|  | 746 | ? LXSTAT (_STAT_VER, name, &st) | 
|  | 747 | : XSTAT (_STAT_VER, name, &st)) < 0) | 
|  | 748 | { | 
|  | 749 | if (!(flags & FTW_PHYS) | 
|  | 750 | && errno == ENOENT | 
|  | 751 | && LXSTAT (_STAT_VER, name, &st) == 0 | 
|  | 752 | && S_ISLNK (st.st_mode)) | 
|  | 753 | result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN], | 
|  | 754 | &data.ftw); | 
|  | 755 | else | 
|  | 756 | /* No need to call the callback since we cannot say anything | 
|  | 757 | about the object.  */ | 
|  | 758 | result = -1; | 
|  | 759 | } | 
|  | 760 | else | 
|  | 761 | { | 
|  | 762 | if (S_ISDIR (st.st_mode)) | 
|  | 763 | { | 
|  | 764 | /* Remember the device of the initial directory in case | 
|  | 765 | FTW_MOUNT is given.  */ | 
|  | 766 | data.dev = st.st_dev; | 
|  | 767 |  | 
|  | 768 | /* We know this directory now.  */ | 
|  | 769 | if (!(flags & FTW_PHYS)) | 
|  | 770 | result = add_object (&data, &st); | 
|  | 771 |  | 
|  | 772 | if (result == 0) | 
|  | 773 | result = ftw_dir (&data, &st, NULL); | 
|  | 774 | } | 
|  | 775 | else | 
|  | 776 | { | 
|  | 777 | int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F; | 
|  | 778 |  | 
|  | 779 | result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag], | 
|  | 780 | &data.ftw); | 
|  | 781 | } | 
|  | 782 | } | 
|  | 783 |  | 
|  | 784 | if ((flags & FTW_ACTIONRETVAL) | 
|  | 785 | && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS)) | 
|  | 786 | result = 0; | 
|  | 787 | } | 
|  | 788 |  | 
|  | 789 | /* Return to the start directory (if necessary).  */ | 
|  | 790 | if (cwdfd != -1) | 
|  | 791 | { | 
|  | 792 | int save_err = errno; | 
|  | 793 | __fchdir (cwdfd); | 
|  | 794 | close_not_cancel_no_status (cwdfd); | 
|  | 795 | __set_errno (save_err); | 
|  | 796 | } | 
|  | 797 | else if (cwd != NULL) | 
|  | 798 | { | 
|  | 799 | int save_err = errno; | 
|  | 800 | __chdir (cwd); | 
|  | 801 | free (cwd); | 
|  | 802 | __set_errno (save_err); | 
|  | 803 | } | 
|  | 804 |  | 
|  | 805 | /* Free all memory.  */ | 
|  | 806 | out_fail: | 
|  | 807 | save_err = errno; | 
|  | 808 | __tdestroy (data.known_objects, free); | 
|  | 809 | free (data.dirbuf); | 
|  | 810 | __set_errno (save_err); | 
|  | 811 |  | 
|  | 812 | return result; | 
|  | 813 | } | 
|  | 814 |  | 
|  | 815 |  | 
|  | 816 |  | 
|  | 817 | /* Entry points.  */ | 
|  | 818 |  | 
|  | 819 | int | 
|  | 820 | FTW_NAME (const char *path, FTW_FUNC_T func, int descriptors) | 
|  | 821 | { | 
|  | 822 | return ftw_startup (path, 0, func, descriptors, 0); | 
|  | 823 | } | 
|  | 824 |  | 
|  | 825 | #ifndef _LIBC | 
|  | 826 | int | 
|  | 827 | NFTW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags) | 
|  | 828 | { | 
|  | 829 | return ftw_startup (path, 1, func, descriptors, flags); | 
|  | 830 | } | 
|  | 831 | #else | 
|  | 832 |  | 
|  | 833 | # include <shlib-compat.h> | 
|  | 834 |  | 
|  | 835 | int NFTW_NEW_NAME (const char *, NFTW_FUNC_T, int, int); | 
|  | 836 |  | 
|  | 837 | int | 
|  | 838 | NFTW_NEW_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags) | 
|  | 839 | { | 
|  | 840 | if (flags | 
|  | 841 | & ~(FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH | FTW_ACTIONRETVAL)) | 
|  | 842 | { | 
|  | 843 | __set_errno (EINVAL); | 
|  | 844 | return -1; | 
|  | 845 | } | 
|  | 846 | return ftw_startup (path, 1, func, descriptors, flags); | 
|  | 847 | } | 
|  | 848 |  | 
|  | 849 | versioned_symbol (libc, NFTW_NEW_NAME, NFTW_NAME, GLIBC_2_3_3); | 
|  | 850 |  | 
|  | 851 | # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3) | 
|  | 852 |  | 
|  | 853 | /* Older nftw* version just ignored all unknown flags.  */ | 
|  | 854 |  | 
|  | 855 | int NFTW_OLD_NAME (const char *, NFTW_FUNC_T, int, int); | 
|  | 856 |  | 
|  | 857 | int | 
|  | 858 | attribute_compat_text_section | 
|  | 859 | NFTW_OLD_NAME (const char *path, NFTW_FUNC_T func, int descriptors, int flags) | 
|  | 860 | { | 
|  | 861 | flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH); | 
|  | 862 | return ftw_startup (path, 1, func, descriptors, flags); | 
|  | 863 | } | 
|  | 864 |  | 
|  | 865 | compat_symbol (libc, NFTW_OLD_NAME, NFTW_NAME, GLIBC_2_1); | 
|  | 866 | # endif | 
|  | 867 | #endif |