source: trunk/libs/newlib/src/newlib/libc/sys/linux/ftw.c

Last change on this file was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

File size: 15.1 KB
Line 
1/* File tree walker functions.
2   Copyright (C) 1996,1997,1998,1999,2000,2001 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/* Modified for newlib by Jeff Johnston, July 26, 2002 */
22
23#define _GNU_SOURCE 1
24
25#include <dirent.h>
26#include <errno.h>
27#include <ftw.h>
28#include <search.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32#include <sys/param.h>
33#include <sys/stat.h>
34
35extern struct dirent64 *__readdir64 (DIR *);
36
37/* #define NDEBUG 1 */
38#include <assert.h>
39
40/* Support for the LFS API version.  */
41#ifndef FTW_NAME
42# define FTW_NAME ftw
43# define NFTW_NAME nftw
44# define INO_T ino_t
45# define STAT stat
46# define LXSTAT lstat
47# define XSTAT stat
48# define FTW_FUNC_T __ftw_func_t
49# define NFTW_FUNC_T __nftw_func_t
50#endif
51
52#define dirfd(x) ((x)->dd_fd)
53
54struct dir_data
55{
56  DIR *stream;
57  char *content;
58};
59
60struct known_object
61{
62  dev_t dev;
63  INO_T ino;
64};
65
66struct ftw_data
67{
68  /* Array with pointers to open directory streams.  */
69  struct dir_data **dirstreams;
70  size_t actdir;
71  size_t maxdir;
72
73  /* Buffer containing name of currently processed object.  */
74  char *dirbuf;
75  size_t dirbufsize;
76
77  /* Passed as fourth argument to `nftw' callback.  The `base' member
78     tracks the content of the `dirbuf'.  */
79  struct FTW ftw;
80
81  /* Flags passed to `nftw' function.  0 for `ftw'.  */
82  int flags;
83
84  /* Conversion array for flag values.  It is the identity mapping for
85     `nftw' calls, otherwise it maps the values to those know by
86     `ftw'.  */
87  const int *cvt_arr;
88
89  /* Callback function.  We always use the `nftw' form.  */
90  NFTW_FUNC_T func;
91
92  /* Device of starting point.  Needed for FTW_MOUNT.  */
93  dev_t dev;
94
95  /* Data structure for keeping fingerprints of already processed
96     object.  This is needed when not using FTW_PHYS.  */
97  void *known_objects;
98};
99
100
101/* Internally we use the FTW_* constants used for `nftw'.  When the
102   process called `ftw' we must reduce the flag to the known flags
103   for `ftw'.  */
104static const int nftw_arr[] =
105{
106  FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
107};
108
109static const int ftw_arr[] =
110{
111  FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
112};
113
114
115/* Forward declarations of local functions.  */
116static int ftw_dir (struct ftw_data *data, struct STAT *st);
117
118
119static int
120object_compare (const void *p1, const void *p2)
121{
122  /* We don't need a sophisticated and useful comparison.  We are only
123     interested in equality.  However, we must be careful not to
124     accidentally compare `holes' in the structure.  */
125  const struct known_object *kp1 = p1, *kp2 = p2;
126  int cmp1;
127  cmp1 = (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
128  if (cmp1 != 0)
129    return cmp1;
130  return (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
131}
132
133
134static inline int
135add_object (struct ftw_data *data, struct STAT *st)
136{
137  struct known_object *newp = malloc (sizeof (struct known_object));
138  if (newp == NULL)
139    return -1;
140  newp->dev = st->st_dev;
141  newp->ino = st->st_ino;
142  return tsearch (newp, &data->known_objects, object_compare) ? 0 : -1;
143}
144
145
146static inline int
147find_object (struct ftw_data *data, struct STAT *st)
148{
149  struct known_object obj = { dev: st->st_dev, ino: st->st_ino };
150  return tfind (&obj, &data->known_objects, object_compare) != NULL;
151}
152
153
154static inline int
155open_dir_stream (struct ftw_data *data, struct dir_data *dirp)
156{
157  int result = 0;
158
159  if (data->dirstreams[data->actdir] != NULL)
160    {
161      /* Oh, oh.  We must close this stream.  Get all remaining
162         entries and store them as a list in the `content' member of
163         the `struct dir_data' variable.  */
164      size_t bufsize = 1024;
165      char *buf = malloc (bufsize);
166
167      if (buf == NULL)
168        result = -1;
169      else
170        {
171          DIR *st = data->dirstreams[data->actdir]->stream;
172          struct dirent64 *d;
173          size_t actsize = 0;
174
175          while ((d = __readdir64 (st)) != NULL)
176            {
177              size_t this_len = strlen (d->d_name);
178              if (actsize + this_len + 2 >= bufsize)
179                {
180                  char *newp;
181                  bufsize += MAX (1024, 2 * this_len);
182                  newp = realloc (buf, bufsize);
183                  if (newp == NULL)
184                    {
185                      /* No more memory.  */
186                      int save_err = errno;
187                      free (buf);
188                      __set_errno (save_err);
189                      result = -1;
190                      break;
191                    }
192                  buf = newp;
193                }
194
195              *((char *) mempcpy (buf + actsize, d->d_name, this_len))
196                = '\0';
197              actsize += this_len + 1;
198            }
199
200          /* Terminate the list with an additional NUL byte.  */
201          buf[actsize++] = '\0';
202
203          /* Shrink the buffer to what we actually need.  */
204          data->dirstreams[data->actdir]->content = realloc (buf, actsize);
205          if (data->dirstreams[data->actdir]->content == NULL)
206            {
207              int save_err = errno;
208              free (buf);
209              __set_errno (save_err);
210              result = -1;
211            }
212          else
213            {
214              closedir (st);
215              data->dirstreams[data->actdir]->stream = NULL;
216              data->dirstreams[data->actdir] = NULL;
217            }
218        }
219    }
220
221  /* Open the new stream.  */
222  if (result == 0)
223    {
224      assert (data->dirstreams[data->actdir] == NULL);
225
226      dirp->stream = opendir (data->dirbuf);
227      if (dirp->stream == NULL)
228        result = -1;
229      else
230        {
231          dirp->content = NULL;
232          data->dirstreams[data->actdir] = dirp;
233
234          if (++data->actdir == data->maxdir)
235            data->actdir = 0;
236        }
237    }
238
239  return result;
240}
241
242
243static inline int
244process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
245               size_t namlen)
246{
247  struct STAT st;
248  int result = 0;
249  int flag = 0;
250
251  if (name[0] == '.' && (name[1] == '\0'
252                         || (name[1] == '.' && name[2] == '\0')))
253    /* Don't process the "." and ".." entries.  */
254    return 0;
255
256  if (data->dirbufsize < data->ftw.base + namlen + 2)
257    {
258      /* Enlarge the buffer.  */
259      char *newp;
260
261      data->dirbufsize *= 2;
262      newp = realloc (data->dirbuf, data->dirbufsize);
263      if (newp == NULL)
264        return -1;
265      data->dirbuf = newp;
266    }
267
268  *((char *) mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
269
270  if (((data->flags & FTW_PHYS)
271       ? LXSTAT (data->dirbuf, &st)
272       : XSTAT (data->dirbuf, &st)) < 0)
273    {
274      if (errno != EACCES && errno != ENOENT)
275        result = -1;
276      else if (!(data->flags & FTW_PHYS)
277               && LXSTAT (data->dirbuf, &st) == 0
278               && S_ISLNK (st.st_mode))
279        flag = FTW_SLN;
280      else
281        flag = FTW_NS;
282    }
283  else
284    {
285      if (S_ISDIR (st.st_mode))
286        flag = FTW_D;
287      else if (S_ISLNK (st.st_mode))
288        flag = FTW_SL;
289      else
290        flag = FTW_F;
291    }
292
293  if (result == 0
294      && (flag == FTW_NS
295          || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev))
296    {
297      if (flag == FTW_D)
298        {
299          if ((data->flags & FTW_PHYS)
300              || (!find_object (data, &st)
301                  /* Remember the object.  */
302                  && (result = add_object (data, &st)) == 0))
303            {
304              result = ftw_dir (data, &st);
305
306              if (result == 0 && (data->flags & FTW_CHDIR))
307                {
308                  /* Change back to current directory.  */
309                  int done = 0;
310                  if (dir->stream != NULL)
311                    if (fchdir (dirfd (dir->stream)) == 0)
312                      done = 1;
313
314                  if (!done)
315                    {
316                      if (data->ftw.base == 1)
317                        {
318                          if (chdir ("/") < 0)
319                            result = -1;
320                        }
321                      else
322                        {
323                          /* Please note that we overwrite a slash.  */
324                          data->dirbuf[data->ftw.base - 1] = '\0';
325
326                          if (chdir (data->dirbuf) < 0)
327                            result = -1;
328
329                          data->dirbuf[data->ftw.base - 1] = '/';
330                        }
331                    }
332                }
333            }
334        }
335      else
336        result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag],
337                                &data->ftw);
338    }
339
340  return result;
341}
342
343
344static int
345ftw_dir (struct ftw_data *data, struct STAT *st)
346{
347  struct dir_data dir;
348  struct dirent64 *d;
349  int previous_base = data->ftw.base;
350  int result;
351  char *startp;
352
353  /* Open the stream for this directory.  This might require that
354     another stream has to be closed.  */
355  result = open_dir_stream (data, &dir);
356  if (result != 0)
357    {
358      if (errno == EACCES)
359        /* We cannot read the directory.  Signal this with a special flag.  */
360        result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw);
361
362      return result;
363    }
364
365  /* First, report the directory (if not depth-first).  */
366  if (!(data->flags & FTW_DEPTH))
367    {
368      result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw);
369      if (result != 0)
370        return result;
371    }
372
373  /* If necessary, change to this directory.  */
374  if (data->flags & FTW_CHDIR)
375    {
376      if (fchdir (dirfd (dir.stream)) < 0)
377        {
378          if (errno == ENOSYS)
379            {
380              if (chdir (data->dirbuf) < 0)
381                result = -1;
382            }
383          else
384            result = -1;
385        }
386
387      if (result != 0)
388        {
389          int save_err = errno;
390          closedir (dir.stream);
391          __set_errno (save_err);
392
393          if (data->actdir-- == 0)
394            data->actdir = data->maxdir - 1;
395          data->dirstreams[data->actdir] = NULL;
396
397          return result;
398        }
399    }
400
401  /* Next, update the `struct FTW' information.  */
402  ++data->ftw.level;
403  startp = strchr (data->dirbuf, '\0');
404  /* There always must be a directory name.  */
405  assert (startp != data->dirbuf);
406  if (startp[-1] != '/')
407    *startp++ = '/';
408  data->ftw.base = startp - data->dirbuf;
409
410  while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL)
411    {
412      result = process_entry (data, &dir, d->d_name, strlen (d->d_name));
413      if (result != 0)
414        break;
415    }
416
417  if (dir.stream != NULL)
418    {
419      /* The stream is still open.  I.e., we did not need more
420         descriptors.  Simply close the stream now.  */
421      int save_err = errno;
422
423      assert (dir.content == NULL);
424
425      closedir (dir.stream);
426      __set_errno (save_err);
427
428      if (data->actdir-- == 0)
429        data->actdir = data->maxdir - 1;
430      data->dirstreams[data->actdir] = NULL;
431    }
432  else
433    {
434      int save_err;
435      char *runp = dir.content;
436
437      while (result == 0 && *runp != '\0')
438        {
439          char *endp = strchr (runp, '\0');
440
441          result = process_entry (data, &dir, runp, endp - runp);
442
443          runp = endp + 1;
444        }
445
446      save_err = errno;
447      free (dir.content);
448      __set_errno (save_err);
449    }
450
451  /* Prepare the return, revert the `struct FTW' information.  */
452  data->dirbuf[data->ftw.base - 1] = '\0';
453  --data->ftw.level;
454  data->ftw.base = previous_base;
455
456  /* Finally, if we process depth-first report the directory.  */
457  if (result == 0 && (data->flags & FTW_DEPTH))
458    result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw);
459
460  return result;
461}
462
463
464static int
465ftw_startup (const char *dir, int is_nftw, void *func, int descriptors,
466             int flags)
467{
468  struct ftw_data data;
469  struct STAT st;
470  int result = 0;
471  int save_err;
472  int len;
473  char *cwd = NULL;
474  char *cp;
475
476  /* First make sure the parameters are reasonable.  */
477  if (dir[0] == '\0')
478    {
479      __set_errno (ENOENT);
480      return -1;
481    }
482
483  if (access (dir, R_OK) != 0)
484    return -1;
485
486  data.maxdir = descriptors < 1 ? 1 : descriptors;
487  data.actdir = 0;
488  data.dirstreams = (struct dir_data **) alloca (data.maxdir
489                                                 * sizeof (struct dir_data *));
490  memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *));
491
492#ifdef PATH_MAX
493  data.dirbufsize = MAX (2 * strlen (dir), PATH_MAX);
494#else
495  data.dirbufsize = 2 * strlen (dir);
496#endif
497  data.dirbuf = (char *) malloc (data.dirbufsize);
498  if (data.dirbuf == NULL)
499    return -1;
500  len = strlen (dir);
501  cp = mempcpy (data.dirbuf, dir, len);
502  /* Strip trailing slashes.  */
503  while (cp > data.dirbuf + 1 && cp[-1] == '/')
504    --cp;
505  *cp = '\0';
506
507  data.ftw.level = 0;
508
509  /* Find basename.  */
510  while (cp > data.dirbuf && cp[-1] != '/')
511    --cp;
512  data.ftw.base = cp - data.dirbuf;
513
514  data.flags = flags;
515
516  /* This assignment might seem to be strange but it is what we want.
517     The trick is that the first three arguments to the `ftw' and
518     `nftw' callback functions are equal.  Therefore we can call in
519     every case the callback using the format of the `nftw' version
520     and get the correct result since the stack layout for a function
521     call in C allows this.  */
522  data.func = (NFTW_FUNC_T) func;
523
524  /* Since we internally use the complete set of FTW_* values we need
525     to reduce the value range before calling a `ftw' callback.  */
526  data.cvt_arr = is_nftw ? nftw_arr : ftw_arr;
527
528  /* No object known so far.  */
529  data.known_objects = NULL;
530
531  /* Now go to the directory containing the initial file/directory.  */
532  if ((flags & FTW_CHDIR) && data.ftw.base > 0)
533    {
534      /* GNU extension ahead.  */
535      cwd =  getcwd (NULL, 0);
536      if (cwd == NULL)
537        result = -1;
538      else
539        {
540          /* Change to the directory the file is in.  In data.dirbuf
541             we have a writable copy of the file name.  Just NUL
542             terminate it for now and change the directory.  */
543          if (data.ftw.base == 1)
544            /* I.e., the file is in the root directory.  */
545            result = chdir ("/");
546          else
547            {
548              char ch = data.dirbuf[data.ftw.base - 1];
549              data.dirbuf[data.ftw.base - 1] = '\0';
550              result = chdir (data.dirbuf);
551              data.dirbuf[data.ftw.base - 1] = ch;
552            }
553        }
554    }
555
556  /* Get stat info for start directory.  */
557  if (result == 0)
558    {
559      if (((flags & FTW_PHYS)
560           ? LXSTAT (data.dirbuf, &st)
561           : XSTAT (data.dirbuf, &st)) < 0)
562        {
563          if (errno == EACCES)
564            result = (*data.func) (data.dirbuf, &st, FTW_NS, &data.ftw);
565          else if (!(flags & FTW_PHYS)
566                   && errno == ENOENT
567                   && LXSTAT (dir, &st) == 0
568                   && S_ISLNK (st.st_mode))
569            result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN],
570                                   &data.ftw);
571          else
572            /* No need to call the callback since we cannot say anything
573               about the object.  */
574            result = -1;
575        }
576      else
577        {
578          if (S_ISDIR (st.st_mode))
579            {
580              /* Remember the device of the initial directory in case
581                 FTW_MOUNT is given.  */
582              data.dev = st.st_dev;
583
584              /* We know this directory now.  */
585              if (!(flags & FTW_PHYS))
586                result = add_object (&data, &st);
587
588              if (result == 0)
589                result = ftw_dir (&data, &st);
590            }
591          else
592            {
593              int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F;
594
595              result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag],
596                                     &data.ftw);
597            }
598        }
599    }
600
601  /* Return to the start directory (if necessary).  */
602  if (cwd != NULL)
603    {
604      int save_err = errno;
605      chdir (cwd);
606      free (cwd);
607      __set_errno (save_err);
608    }
609
610  /* Free all memory.  */
611  save_err = errno;
612  tdestroy (data.known_objects, free);
613  free (data.dirbuf);
614  __set_errno (save_err);
615
616  return result;
617}
618
619
620
621/* Entry points.  */
622
623int
624FTW_NAME (path, func, descriptors)
625     const char *path;
626     FTW_FUNC_T func;
627     int descriptors;
628{
629  return ftw_startup (path, 0, func, descriptors, 0);
630}
631
632int
633NFTW_NAME (path, func, descriptors, flags)
634     const char *path;
635     NFTW_FUNC_T func;
636     int descriptors;
637     int flags;
638{
639  return ftw_startup (path, 1, func, descriptors, flags);
640}
Note: See TracBrowser for help on using the repository browser.