2003-11-04 Jakub Jelinek * io/ftw.c (ftw_dir): Close dir if callback with FTW_D type returns non-zero. * io/Makefile (tests): Add bug-ftw4. * io/bug-ftw4.c: New test. --- libc/io/Makefile.jj 2003-10-29 00:18:42.000000000 +0100 +++ libc/io/Makefile 2003-11-04 19:40:10.000000000 +0100 @@ -57,7 +57,7 @@ static-only-routines = stat fstat lstat others := pwd test-srcs := ftwtest tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \ - tst-fcntl bug-ftw1 bug-ftw2 bug-ftw3 + tst-fcntl bug-ftw1 bug-ftw2 bug-ftw3 bug-ftw4 distribute := ftwtest-sh --- libc/io/ftw.c.jj 2003-06-11 23:48:58.000000000 +0200 +++ libc/io/ftw.c 2003-11-04 19:57:42.000000000 +0100 @@ -474,7 +474,19 @@ ftw_dir (struct ftw_data *data, struct S { result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw); if (result != 0) - return result; + { + int save_err; +fail: + save_err = errno; + __closedir (dir.stream); + __set_errno (save_err); + + if (data->actdir-- == 0) + data->actdir = data->maxdir - 1; + data->dirstreams[data->actdir] = NULL; + + return result; + } } /* If necessary, change to this directory. */ @@ -492,17 +504,7 @@ ftw_dir (struct ftw_data *data, struct S } if (result != 0) - { - int save_err = errno; - __closedir (dir.stream); - __set_errno (save_err); - - if (data->actdir-- == 0) - data->actdir = data->maxdir - 1; - data->dirstreams[data->actdir] = NULL; - - return result; - } + goto fail; } /* Next, update the `struct FTW' information. */ --- libc/io/bug-ftw4.c.jj 2003-11-04 19:39:53.000000000 +0100 +++ libc/io/bug-ftw4.c 2003-11-04 19:43:57.000000000 +0100 @@ -0,0 +1,124 @@ +/* Test if ftw function doesn't leak fds. + Copyright (C) 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek , 2003. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include +#include + +static int cb_called; + +static int +cb (const char *name, const struct stat64 *st, int type) +{ + return cb_called++ & 1; +} + +int +main (void) +{ + char name[32] = "/tmp/ftwXXXXXX", *p; + int ret, i, result = 0, fd, fd1, fd2; + + if (mkdtemp (name) == NULL) + { + printf ("Couldn't make temporary directory: %m\n"); + exit (EXIT_FAILURE); + } + p = strchr (name, '\0'); + strcpy (p, "/1"); + if (mkdir (name, 0755) < 0) + { + printf ("Couldn't make temporary subdirectory: %m\n"); + exit (EXIT_FAILURE); + } + *p = '\0'; + + ret = ftw64 (name, cb, 20); + if (ret != 1) + { + printf ("ftw64 returned %d instead of 1", ret); + result = 1; + } + + fd = open (name, O_RDONLY); + if (fd < 0) + { + printf ("open failed: %m\n"); + result = 1; + } + fd1 = open (name, O_RDONLY); + if (fd1 < 0) + { + printf ("open failed: %m\n"); + result = 1; + } + else + close (fd1); + if (fd >= 0) + close (fd); + + for (i = 0; i < 128; ++i) + { + ret = ftw64 (name, cb, 20); + if (ret != 1) + { + printf ("ftw64 returned %d instead of 1", ret); + result = 1; + } + } + + fd = open (name, O_RDONLY); + if (fd < 0) + { + printf ("open failed: %m\n"); + result = 1; + } + fd2 = open (name, O_RDONLY); + if (fd2 < 0) + { + printf ("open failed: %m\n"); + result = 1; + } + else + close (fd2); + if (fd >= 0) + close (fd); + + if (fd2 >= fd1 + 128) + { + printf ("ftw64 leaking fds: %d -> %d\n", fd1, fd2); + result = 1; + } + + if (cb_called != 129 * 2) + { + printf ("callback called %d times\n", cb_called); + result = 1; + } + + strcpy (p, "/1"); + rmdir (name); + *p = '\0'; + rmdir (name); + return result; +}