bug-glibc
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

ftw bug+fix: failure with relative path and FTW_CHDIR


From: Jim Meyering
Subject: ftw bug+fix: failure with relative path and FTW_CHDIR
Date: Sat, 11 Jan 2003 17:41:44 +0100

Whenever one calls ftw (or nftw) with a relative directory name
and with the FTW_CHDIR option enabled, on e.g. a directory `d'
with sub-hierarchy `s/t', it mistakenly tries to opendir `d/s'
after changing into that directory -- so of course it fails.

BTW, the test script (io/ftwtest-sh) would exercise this case
if it used a temporary directory with a relative name.

The fix (below) solves the problem and at the same time removes
an O(N^2) factor in the performance of ftw (N is the number of
file name components referenced by calls like stat and open).
Of course, for most uses, N is small enough that the N^2 component
doesn't make a big difference, but in some pathological cases it
probably does.

Here's a demonstration of the problem:
(notice that the second lstat fails and we get only two lines of output)

  pi$ mkdir -p d/s/t
  pi$ gcc -D_GNU_SOURCE -O -Wall ftw-test.c ftw-orig.c
  pi$ strace -e lstat64 ./a.out d
  lstat64("d", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
  d
  lstat64("d/s", 0xbfffef80)         = -1 ENOENT (No such file or directory)
  d/s

And this shows that the fixed version works:

  pi$ gcc -D_GNU_SOURCE -O -Wall ftw-test.c ftw.c
  pi$ strace -e lstat64 ./a.out d
  lstat64("d", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
  d
  lstat64("s", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
  d/s
  lstat64("t", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
  d/s/t

==================
First the tiny driver code:

pi$ cat ftw-test.c
#include <stdio.h>
#include <stdlib.h>
#include <ftw.h>

static int
cb (const char *file, const struct stat *sb, int file_type, struct FTW *info)
{
  printf ("%s\n", file);
  return 0;
}

int
main (int argc, char **argv)
{
  int flags = FTW_PHYS | FTW_MOUNT | FTW_CHDIR;
  int err;
  if (argc < 2)
    exit (2);
  err = nftw (argv[1], cb, 30, flags);
  exit (err);
}

==================
Here's the bug fix, along with some portability
changes needed to make ftw.c compile outside of glibc.
(the file, ftw-orig.c, used above, is the current libc/io/ftw.c
with only the following portability changes)

2003-01-11  Jim Meyering  <address@hidden>

        * io/ftw.c [HAVE_CONFIG_H]: Include <config.h>.
        [HAVE_SYS_PARAM_H || _LIBC]: Guard inclusion of <sys/param.h>.
        Include <sys/stat.h>, not <include/sys/stat.h>.
        [!_LIBC] (__chdir, __closedir, __fchdir, __getcwd, __opendir): Define.
        [!_LIBC] (__readdir64, __tdestroy, __tfind, __tsearch): Define.
        [!_LIBC] (internal_function, dirent64, MAX): Define.
        (__set_errno): Define if not already defined.
        (open_dir_stream): When FTW_CHDIR is enabled, invoke opendir on
        the basename, not the entire file name.
        (process_entry): When FTW_CHDIR is enabled, invoke XSTAT or LXSTAT on
        the basename, not the entire file name.

Index: io/ftw.c
===================================================================
RCS file: /cvs/libc/io/ftw.c,v
retrieving revision 1.33
diff -u -p -u -r1.33 ftw.c
--- io/ftw.c    17 Dec 2002 00:04:53 -0000      1.33
+++ io/ftw.c    11 Jan 2003 16:26:57 -0000
@@ -18,6 +18,10 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
 #include <dirent.h>
 #include <errno.h>
 #include <ftw.h>
@@ -25,12 +29,45 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <sys/param.h>
-#include <include/sys/stat.h>
+#if HAVE_SYS_PARAM_H || defined _LIBC
+# include <sys/param.h>
+#endif
+#include <sys/stat.h>
 
 /* #define NDEBUG 1 */
 #include <assert.h>
 
+#ifndef _LIBC
+# undef __chdir
+# define __chdir chdir
+# undef __closedir
+# define __closedir closedir
+# undef __fchdir
+# define __fchdir fchdir
+# undef __getcwd
+# define __getcwd getcwd
+# undef __opendir
+# define __opendir opendir
+# undef __readdir64
+# define __readdir64 readdir
+# undef __tdestroy
+# define __tdestroy tdestroy
+# undef __tfind
+# define __tfind tfind
+# undef __tsearch
+# define __tsearch tsearch
+# undef internal_function
+# define internal_function /* empty */
+# undef dirent64
+# define dirent64 dirent
+# undef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef __set_errno
+# define __set_errno(Val) errno = (Val)
+#endif
+
 /* Support for the LFS API version.  */
 #ifndef FTW_NAME
 # define FTW_NAME ftw
@@ -213,9 +250,11 @@ open_dir_stream (struct ftw_data *data, 
   /* Open the new stream.  */
   if (result == 0)
     {
+      const char *name = ((data->flags & FTW_CHDIR)
+                         ? data->dirbuf + data->ftw.base: data->dirbuf);
       assert (data->dirstreams[data->actdir] == NULL);
 
-      dirp->stream = __opendir (data->dirbuf);
+      dirp->stream = __opendir (name);
       if (dirp->stream == NULL)
        result = -1;
       else
@@ -259,14 +298,17 @@ process_entry (struct ftw_data *data, st
 
   *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';
 
+  if ( ! (data->flags & FTW_CHDIR))
+    name = data->dirbuf;
+
   if (((data->flags & FTW_PHYS)
-       ? LXSTAT (_STAT_VER, data->dirbuf, &st)
-       : XSTAT (_STAT_VER, data->dirbuf, &st)) < 0)
+       ? LXSTAT (_STAT_VER, name, &st)
+       : XSTAT (_STAT_VER, name, &st)) < 0)
     {
       if (errno != EACCES && errno != ENOENT)
        result = -1;
       else if (!(data->flags & FTW_PHYS)
-              && LXSTAT (_STAT_VER, data->dirbuf, &st) == 0
+              && LXSTAT (_STAT_VER, name, &st) == 0
               && S_ISLNK (st.st_mode))
        flag = FTW_SLN;
       else

Attachment: pgpplpJ7oupEE.pgp
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]