• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.5/libatalk/util/
1/* File tree walker functions.
2   Copyright (C) 1996-2004, 2006-2008, 2010 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, write to the Free
18   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19   02111-1307 USA.  */
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#if __GNUC__
26# define alloca __builtin_alloca
27#else
28#  include <alloca.h>
29#endif
30
31#include <dirent.h>
32#define NAMLEN(dirent) strlen ((dirent)->d_name)
33#include <errno.h>
34#include <fcntl.h>
35#include <limits.h>
36#include <search.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <sys/stat.h>
41
42#if HAVE_SYS_PARAM_H
43# include <sys/param.h>
44#endif
45
46#include <atalk/ftw.h>
47
48#define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
49
50#define NDEBUG 1
51#include <assert.h>
52
53#ifndef _LIBC
54# undef __chdir
55# define __chdir chdir
56# undef __closedir
57# define __closedir closedir
58# undef __fchdir
59# define __fchdir fchdir
60# undef __getcwd
61# define __getcwd(P, N) xgetcwd ()
62# undef __mempcpy
63# define __mempcpy mempcpy
64# undef __opendir
65# define __opendir opendir
66# undef __readdir64
67# define __readdir64 readdir
68# undef __tdestroy
69# define __tdestroy tdestroy
70# undef __tfind
71# define __tfind tfind
72# undef __tsearch
73# define __tsearch tsearch
74# undef internal_function
75# define internal_function /* empty */
76# undef dirent64
77# define dirent64 dirent
78# undef MAX
79# define MAX(a, b) ((a) > (b) ? (a) : (b))
80#endif
81
82#ifndef __set_errno
83# define __set_errno(Val) errno = (Val)
84#endif
85
86/* Support for the LFS API version.  */
87#ifndef FTW_NAME
88# define FTW_NAME ftw
89# define NFTW_NAME nftw
90# define NFTW_OLD_NAME __old_nftw
91# define NFTW_NEW_NAME __new_nftw
92# define INO_T ino_t
93# define STAT stat
94# define LXSTAT(V,f,sb) lstat (f,sb)
95# define XSTAT(V,f,sb) stat (f,sb)
96# define FXSTATAT(V,d,f,sb,m) fstatat (d, f, sb, m)
97
98#endif
99
100/* We define PATH_MAX if the system does not provide a definition.
101   This does not artificially limit any operation.  PATH_MAX is simply
102   used as a guesstimate for the expected maximal path length.
103   Buffers will be enlarged if necessary.  */
104#ifndef PATH_MAX
105# define PATH_MAX 1024
106#endif
107
108struct dir_data
109{
110    DIR *stream;
111    int streamfd;
112    char *content;
113};
114
115struct known_object
116{
117    dev_t dev;
118    INO_T ino;
119};
120
121struct ftw_data
122{
123    /* Array with pointers to open directory streams.  */
124    struct dir_data **dirstreams;
125    size_t actdir;
126    size_t maxdir;
127
128    /* Buffer containing name of currently processed object.  */
129    char *dirbuf;
130    size_t dirbufsize;
131
132    /* Passed as fourth argument to `nftw' callback.  The `base' member
133       tracks the content of the `dirbuf'.  */
134    struct FTW ftw;
135
136    /* Flags passed to `nftw' function.  0 for `ftw'.  */
137    int flags;
138
139    /* Conversion array for flag values.  It is the identity mapping for
140       `nftw' calls, otherwise it maps the values to those known by
141       `ftw'.  */
142    const int *cvt_arr;
143
144    /* Callback function.  We always use the `nftw' form.  */
145    NFTW_FUNC_T func;
146
147    /* Device of starting point.  Needed for FTW_MOUNT.  */
148    dev_t dev;
149
150    /* Data structure for keeping fingerprints of already processed
151       object.  This is needed when not using FTW_PHYS.  */
152    void *known_objects;
153};
154
155
156/* Internally we use the FTW_* constants used for `nftw'.  When invoked
157   as `ftw', map each flag to the subset of values used by `ftw'.  */
158static const int nftw_arr[] =
159{
160    FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
161};
162
163static const int ftw_arr[] =
164{
165    FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
166};
167
168
169static dir_notification_func_t upfunc;
170
171/* Forward declarations of local functions.  */
172static int ftw_dir (struct ftw_data *data, struct STAT *st,
173                    struct dir_data *old_dir) internal_function;
174
175typedef void (*__free_fn_t) (void *__nodep);
176typedef struct node_t {
177    const void *key;
178    struct node_t *left;
179    struct node_t *right;
180    unsigned int red:1;
181} *node;
182
183static void tdestroy_recurse (node root, __free_fn_t freefct)
184{
185    if (root->left != NULL)
186        tdestroy_recurse (root->left, freefct);
187    if (root->right != NULL)
188        tdestroy_recurse (root->right, freefct);
189    (*freefct) ((void *) root->key);
190    /* Free the node itself.  */
191    free (root);
192}
193
194static void mytdestroy (void *vroot, __free_fn_t freefct)
195{
196    node root = (node) vroot;
197
198    if (root != NULL)
199        tdestroy_recurse (root, freefct);
200}
201
202static char *mystpcpy(char *a, const char *b)
203{
204    strcpy(a, b);
205    return (a + strlen(a));
206}
207
208static char *xgetcwd(void)
209{
210    char *cwd;
211    char *ret;
212    unsigned path_max;
213
214    errno = 0;
215    path_max = (unsigned) PATH_MAX;
216    path_max += 2;        /* The getcwd docs say to do this. */
217
218    cwd = malloc (path_max);
219    errno = 0;
220    while ((ret = getcwd (cwd, path_max)) == NULL && errno == ERANGE) {
221        path_max += 512;
222        cwd = realloc (cwd, path_max);
223        errno = 0;
224    }
225
226    if (ret == NULL) {
227        int save_errno = errno;
228        free (cwd);
229        errno = save_errno;
230        return NULL;
231    }
232    return cwd;
233}
234
235static int
236object_compare (const void *p1, const void *p2)
237{
238    /* We don't need a sophisticated and useful comparison.  We are only
239       interested in equality.  However, we must be careful not to
240       accidentally compare `holes' in the structure.  */
241    const struct known_object *kp1 = p1, *kp2 = p2;
242    int cmp1;
243    cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
244    if (cmp1 != 0)
245        return cmp1;
246    return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
247}
248
249
250static int
251add_object (struct ftw_data *data, struct STAT *st)
252{
253    struct known_object *newp = malloc (sizeof (struct known_object));
254    if (newp == NULL)
255        return -1;
256    newp->dev = st->st_dev;
257    newp->ino = st->st_ino;
258    return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1;
259}
260
261
262static inline int
263find_object (struct ftw_data *data, struct STAT *st)
264{
265    struct known_object obj;
266    obj.dev = st->st_dev;
267    obj.ino = st->st_ino;
268    return __tfind (&obj, &data->known_objects, object_compare) != NULL;
269}
270
271
272static inline int
273open_dir_stream (int *dfdp, struct ftw_data *data, struct dir_data *dirp)
274{
275    int result = 0;
276
277    if (data->dirstreams[data->actdir] != NULL)
278    {
279        /* Oh, oh.  We must close this stream.  Get all remaining
280           entries and store them as a list in the `content' member of
281           the `struct dir_data' variable.  */
282        size_t bufsize = 1024;
283        char *buf = malloc (bufsize);
284
285        if (buf == NULL)
286            result = -1;
287        else
288        {
289            DIR *st = data->dirstreams[data->actdir]->stream;
290            struct dirent64 *d;
291            size_t actsize = 0;
292
293            while ((d = __readdir64 (st)) != NULL)
294            {
295                size_t this_len = NAMLEN (d);
296                if (actsize + this_len + 2 >= bufsize)
297                {
298                    char *newp;
299                    bufsize += MAX (1024, 2 * this_len);
300                    newp = (char *) realloc (buf, bufsize);
301                    if (newp == NULL)
302                    {
303                        /* No more memory.  */
304                        int save_err = errno;
305                        free (buf);
306                        __set_errno (save_err);
307                        return -1;
308                    }
309                    buf = newp;
310                }
311
312                *((char *) __mempcpy (buf + actsize, d->d_name, this_len))
313                    = '\0';
314                actsize += this_len + 1;
315            }
316
317            /* Terminate the list with an additional NUL byte.  */
318            buf[actsize++] = '\0';
319
320            /* Shrink the buffer to what we actually need.  */
321            data->dirstreams[data->actdir]->content = realloc (buf, actsize);
322            if (data->dirstreams[data->actdir]->content == NULL)
323            {
324                int save_err = errno;
325                free (buf);
326                __set_errno (save_err);
327                result = -1;
328            }
329            else
330            {
331                __closedir (st);
332                data->dirstreams[data->actdir]->stream = NULL;
333                data->dirstreams[data->actdir]->streamfd = -1;
334                data->dirstreams[data->actdir] = NULL;
335            }
336        }
337    }
338
339    /* Open the new stream.  */
340    if (result == 0)
341    {
342        assert (data->dirstreams[data->actdir] == NULL);
343
344        if (dfdp != NULL && *dfdp != -1)
345        {
346            int fd = openat(*dfdp, data->dirbuf + data->ftw.base, O_RDONLY);
347            dirp->stream = NULL;
348            if (fd != -1 && (dirp->stream = fdopendir (fd)) == NULL)
349                close(fd);
350        }
351        else
352        {
353            const char *name;
354
355            if (data->flags & FTW_CHDIR)
356            {
357                name = data->dirbuf + data->ftw.base;
358                if (name[0] == '\0')
359                    name = ".";
360            }
361            else
362                name = data->dirbuf;
363
364            dirp->stream = __opendir (name);
365        }
366
367        if (dirp->stream == NULL)
368            result = -1;
369        else
370        {
371            dirp->streamfd = dirfd (dirp->stream);
372            dirp->content = NULL;
373            data->dirstreams[data->actdir] = dirp;
374
375            if (++data->actdir == data->maxdir)
376                data->actdir = 0;
377        }
378    }
379
380    return result;
381}
382
383
384static int
385process_entry (struct ftw_data *data, struct dir_data *dir, const char *name, size_t namlen)
386{
387    struct STAT st;
388    int result = 0;
389    int flag = 0;
390    size_t new_buflen;
391
392    if (name[0] == '.' && (name[1] == '\0'
393                           || (name[1] == '.' && name[2] == '\0')))
394        /* Don't process the "." and ".." entries.  */
395        return 0;
396
397    new_buflen = data->ftw.base + namlen + 2;
398    if (data->dirbufsize < new_buflen)
399    {
400        /* Enlarge the buffer.  */
401        char *newp;
402
403        data->dirbufsize = 2 * new_buflen;
404        newp = (char *) realloc (data->dirbuf, data->dirbufsize);
405        if (newp == NULL)
406            return -1;
407        data->dirbuf = newp;
408    }
409
410    *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
411
412    int statres;
413    if (dir->streamfd != -1)
414        statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st,
415                            (data->flags & FTW_PHYS) ? AT_SYMLINK_NOFOLLOW : 0);
416    else
417    {
418        if ((data->flags & FTW_CHDIR) == 0)
419            name = data->dirbuf;
420
421        statres = ((data->flags & FTW_PHYS)
422                   ? LXSTAT (_STAT_VER, name, &st)
423                   : XSTAT (_STAT_VER, name, &st));
424    }
425
426    if (statres < 0)
427    {
428        if (errno != EACCES && errno != ENOENT)
429            result = -1;
430        else if (data->flags & FTW_PHYS)
431            flag = FTW_NS;
432        else
433        {
434            if (dir->streamfd != -1)
435                statres = FXSTATAT (_STAT_VER, dir->streamfd, name, &st,
436                                    AT_SYMLINK_NOFOLLOW);
437            else
438                statres = LXSTAT (_STAT_VER, name, &st);
439            if (statres == 0 && S_ISLNK (st.st_mode))
440                flag = FTW_SLN;
441            else
442                flag = FTW_NS;
443        }
444    }
445    else
446    {
447        if (S_ISDIR (st.st_mode))
448            flag = FTW_D;
449        else if (S_ISLNK (st.st_mode))
450            flag = FTW_SL;
451        else
452            flag = FTW_F;
453    }
454
455    if (result == 0
456        && (flag == FTW_NS
457            || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev))
458    {
459        if (flag == FTW_D)
460        {
461            if ((data->flags & FTW_PHYS)
462                || (!find_object (data, &st)
463                    /* Remember the object.  */
464                    && (result = add_object (data, &st)) == 0))
465                result = ftw_dir (data, &st, dir);
466        }
467        else
468            result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag],
469                                    &data->ftw);
470    }
471
472    if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
473        result = 0;
474
475    return result;
476}
477
478
479static int
480ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir)
481{
482    struct dir_data dir;
483    struct dirent64 *d;
484    int previous_base = data->ftw.base;
485    int result;
486    char *startp;
487
488    /* Open the stream for this directory.  This might require that
489       another stream has to be closed.  */
490    result = open_dir_stream (old_dir == NULL ? NULL : &old_dir->streamfd,
491                              data, &dir);
492    if (result != 0)
493    {
494        if (errno == EACCES)
495            /* We cannot read the directory.  Signal this with a special flag.  */
496            result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw);
497
498        return result;
499    }
500
501    /* First, report the directory (if not depth-first).  */
502    if (!(data->flags & FTW_DEPTH))
503    {
504        result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw);
505        if (result != 0)
506        {
507            int save_err;
508        fail:
509            save_err = errno;
510            __closedir (dir.stream);
511            dir.streamfd = -1;
512            __set_errno (save_err);
513
514            if (data->actdir-- == 0)
515                data->actdir = data->maxdir - 1;
516            data->dirstreams[data->actdir] = NULL;
517            return result;
518        }
519    }
520
521    /* If necessary, change to this directory.  */
522    if (data->flags & FTW_CHDIR)
523    {
524        if (__fchdir (dirfd (dir.stream)) < 0)
525        {
526            result = -1;
527            goto fail;
528        }
529    }
530
531    /* Next, update the `struct FTW' information.  */
532    ++data->ftw.level;
533    startp = data->dirbuf + strlen(data->dirbuf);
534    /* There always must be a directory name.  */
535    assert (startp != data->dirbuf);
536    if (startp[-1] != '/')
537        *startp++ = '/';
538    data->ftw.base = startp - data->dirbuf;
539
540    while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL)
541    {
542        result = process_entry (data, &dir, d->d_name, NAMLEN (d));
543        if (result != 0)
544            break;
545    }
546
547    if (dir.stream != NULL)
548    {
549        /* The stream is still open.  I.e., we did not need more
550           descriptors.  Simply close the stream now.  */
551        int save_err = errno;
552
553        assert (dir.content == NULL);
554
555        __closedir (dir.stream);
556        dir.streamfd = -1;
557        __set_errno (save_err);
558
559        if (data->actdir-- == 0)
560            data->actdir = data->maxdir - 1;
561        data->dirstreams[data->actdir] = NULL;
562    }
563    else
564    {
565        int save_err;
566        char *runp = dir.content;
567
568        while (result == 0 && *runp != '\0')
569        {
570            char *endp = strchr (runp, '\0');
571
572            // XXX Should store the d_type values as well?!
573            result = process_entry (data, &dir, runp, endp - runp);
574
575            runp = endp + 1;
576        }
577
578        save_err = errno;
579        free (dir.content);
580        __set_errno (save_err);
581    }
582
583    if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
584        result = 0;
585
586    /* Prepare the return, revert the `struct FTW' information.  */
587    data->dirbuf[data->ftw.base - 1] = '\0';
588    --data->ftw.level;
589    if (upfunc)
590        (*upfunc)();
591    data->ftw.base = previous_base;
592
593    /* Finally, if we process depth-first report the directory.  */
594    if (result == 0 && (data->flags & FTW_DEPTH))
595        result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw);
596
597    if (old_dir
598        && (data->flags & FTW_CHDIR)
599        && (result == 0
600            || ((data->flags & FTW_ACTIONRETVAL)
601                && (result != -1 && result != FTW_STOP))))
602    {
603        /* Change back to the parent directory.  */
604        int done = 0;
605        if (old_dir->stream != NULL)
606            if (__fchdir (dirfd (old_dir->stream)) == 0)
607                done = 1;
608
609        if (!done)
610        {
611            if (data->ftw.base == 1)
612            {
613                if (__chdir ("/") < 0)
614                    result = -1;
615            }
616            else
617                if (__chdir ("..") < 0)
618                    result = -1;
619        }
620    }
621
622    return result;
623}
624
625
626static int ftw_startup (const char *dir,
627                        int is_nftw,
628                        void *func,
629                        dir_notification_func_t up,
630                        int descriptors,
631                        int flags)
632{
633    struct ftw_data data;
634    struct STAT st;
635    int result = 0;
636    int save_err;
637    int cwdfd = -1;
638    char *cwd = NULL;
639    char *cp;
640
641    upfunc = up;
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 = mystpcpy (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);
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(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.  */
806out_fail:
807    save_err = errno;
808    mytdestroy (data.known_objects, free);
809    free (data.dirbuf);
810    __set_errno (save_err);
811
812    return result;
813}
814
815
816
817/* Entry points.  */
818int NFTW_NAME(const char *path,
819              NFTW_FUNC_T func,
820              dir_notification_func_t up,
821              int descriptors,
822              int flags)
823{
824    return ftw_startup (path, 1, func, up, descriptors, flags);
825}
826
827