summaryrefslogtreecommitdiff
path: root/lib/fdopendir.c
diff options
context:
space:
mode:
authorJim Meyering <meyering@redhat.com>2010-10-08 18:42:59 +0200
committerJim Meyering <meyering@redhat.com>2010-10-08 22:04:06 +0200
commita1885e2dc2a8f1c79ead4e6ceca1a53827b89048 (patch)
tree00f1e42aa371b36c4c17842a5ce628ad201e7dcd /lib/fdopendir.c
parentfe621e2e212dbedcbc80f5b7991717b18d5d8443 (diff)
downloadgnulib-a1885e2dc2a8f1c79ead4e6ceca1a53827b89048.tar.gz
fdopendir: fix a bug on systems lacking openat and /proc support
OpenBSD 4.7 is one such system. The most noticeable effect was failure of any application making nontrivial use of fts: rm, du, chown, chmod etc. E.g., "mkdir -p a/b; ./rm -rf a" would fail with ./rm: traversal failed: `a': Bad file descriptor Debugging that, you see that even though FD 6 was closed just prior to the opendir call in fd_clone_opendir, its resulting dir->dd_fd was 8, rather than the expected value of 6: Breakpoint 3, fdopendir_with_dup (fd=6, older_dupfd=-1) at fdopendir.c:93 93 close (fd); (gdb) n 94 dir = fd_clone_opendir (dupfd); (gdb) n 95 saved_errno = errno; (gdb) p dir->dd_fd $11 = 8 Notice how it closes FD 6, then gets a DIR* pointer using FD 8. The problem is that on OpenBSD, fd_clone_opendir has to resort to using the old-style save/restore CWD mechanism, due to its lack of openat/proc support, and *that* would steal the FD (6) that opendir was supposed to use. The fix is to squirrel away the desired FD so that save_cwd uses a different one, and then free the dest FD right before calling opendir. That guarantees opendir will use the required file descriptor. * lib/fdopendir.c (fd_clone_opendir): Handle the above.
Diffstat (limited to 'lib/fdopendir.c')
-rw-r--r--lib/fdopendir.c14
1 files changed, 14 insertions, 0 deletions
diff --git a/lib/fdopendir.c b/lib/fdopendir.c
index 4adef63791..59826ae4aa 100644
--- a/lib/fdopendir.c
+++ b/lib/fdopendir.c
@@ -140,10 +140,23 @@ fd_clone_opendir (int fd)
dir = opendir (name);
saved_errno = errno;
# else /* !REPLACE_FCHDIR */
+
+ /* Occupy the destination FD slot, so that save_cwd cannot hijack it. */
+ int fd_reserve = dup (fd);
+ if (fd_reserve < 0)
+ {
+ saved_errno = errno;
+ dir = NULL;
+ goto fail;
+ }
+
struct saved_cwd saved_cwd;
if (save_cwd (&saved_cwd) != 0)
openat_save_fail (errno);
+ /* Liberate the target file descriptor, so that opendir uses it. */
+ close (fd_reserve);
+
if (fchdir (fd) != 0)
{
dir = NULL;
@@ -162,6 +175,7 @@ fd_clone_opendir (int fd)
# endif /* !REPLACE_FCHDIR */
}
+ fail:
if (proc_file != buf)
free (proc_file);
errno = saved_errno;