[Top][All Lists]
[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
pgpplpJ7oupEE.pgp
Description: PGP signature
- ftw bug+fix: failure with relative path and FTW_CHDIR,
Jim Meyering <=