]> arthur.barton.de Git - netatalk.git/blob - libatalk/util/ftw.c
Merge branch-2-1
[netatalk.git] / libatalk / util / ftw.c
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
108 struct dir_data
109 {
110     DIR *stream;
111     int streamfd;
112     char *content;
113 };
114
115 struct known_object
116 {
117     dev_t dev;
118     INO_T ino;
119 };
120
121 struct 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'.  */
158 static const int nftw_arr[] =
159 {
160     FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
161 };
162
163 static const int ftw_arr[] =
164 {
165     FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
166 };
167
168
169 static dir_notification_func_t upfunc;
170
171 /* Forward declarations of local functions.  */
172 static int ftw_dir (struct ftw_data *data, struct STAT *st,
173                     struct dir_data *old_dir) internal_function;
174
175 typedef void (*__free_fn_t) (void *__nodep);
176 typedef 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
183 static 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
194 static 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
202 static char *mystpcpy(char *a, const char *b)
203 {
204     strcpy(a, b);
205     return (a + strlen(a));
206 }
207
208 static 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
235 static int
236 object_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
250 static int
251 add_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
262 static inline int
263 find_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
272 static inline int
273 open_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
384 static int
385 process_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
479 static int
480 ftw_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
626 static 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.  */
806 out_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.  */
818 int 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