[Top][All Lists]

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

autotest vs. in-use directories

From: Eric Blake
Subject: autotest vs. in-use directories
Date: Thu, 9 Apr 2009 17:21:58 +0000 (UTC)
User-agent: Loom/3.14 (

I discovered this on cygwin 1.7, which is attempting to add the ability to 
emulate deleting a directory even when a process owns a handle to that 
directory.  Cygwin 1.5 flat out fails, as permitted by POSIX, because Windows 
does not permit deleting an open handle, and autotest already has code that 
gracefully deals with this failure.  But the cygwin 1.7 emulation works by 
detecting failure to delete because of an open handle, and uses a fallback of 
renaming the affected handle to move it into the recycle bin; in most cases, 
this makes it appear that an in-use handle has been successfully unlinked - no 
new process can access the deleted contents, but existing processes can take as 
long as they need to hold on to the file, and the file finally disappears when 
the last handle closes.  Unfortunately, with the current state of cygwin 1.7, 
the rename does not affect subsequent readdir, so a listing of the parent 
directory still shows the supposedly deleted subdir; also, it does not permit 
creating a new file or directory with the name of the old deleted file.  As a 
result, several tests fail on cygwin 1.7 that used to pass on cygwin 1.5; all 
of them have the semantics similar to:

(cd micro-test.dir/1 && ./run)

The problem is that the shell invoking ./run must stick around to wait for its 
return status, thus keeping a handle open on micro-test.dir.  However, the 
autotest-generated run scripts proceed to cd back to the top testsuite 
directory, nuke the per-test directory, and repopulate it from scratch.  Under 
cygwin 1.5, nuking the per-test directory fails (it is still in use by the 
shell waiting for ./run to complete), but we ignore that failure and are able 
to reuse the (now-empty) directory, effectively repopulating it from scratch 
anyways.  But under cygwin 1.7, the nuke succeeds, but we are then unable to 
recreate the directory because of the Windows limitation mentioned above.  
Changing the testsuite to use 'exec ./run' instead of './run' solves the 
problem, because there is no longer a parent shell waiting for status from 
within that directory, and thus no longer any process keeping the directory 
handle in use across run's attempt to nuke and rebuild the per-test directory.

But in thinking about the issue, it affects more than just cygwin.  Consider a 
Unix system with an NFS mount (I tested on Solaris 8).  There is no restriction 
against deleting a directory that is in use by another process, nor against 
recreating a new directory by the same name.  But the process that had the 
directory ripped out from under it does NOT see the recreated directory of the 
same name:

1$ cd /tmp
1$ mkdir foo
1$ cd foo
1$ touch bar
1$ ls

2$ cd /tmp
2$ rm -Rf foo
2$ mkdir foo
2$ cd foo
2$ touch blah
2$ ls

1$ ls
ls: reading directory .: Stale NFS file handle

In other words, the fact that testsuite is trying to nuke the _entire_ per-test 
directory, rather than just its contents, means that any user who does 'cd 
testsuite.dir/nnn; ./run' has given their current shell a stale directory 
handle for $PWD.

So I'm thinking about applying this patch.  Rather than changing autoconf's 
testsuite to use 'exec ./run' (which does indeed make the failing tests once 
again pass for cygwin 1.7, but doesn't help the interactive user who won't want 
to end their session by using exec), I decided to fix autotest to quit trying 
to remove the entire directory, but instead only remove its contents.  I 
believe I got the glob correct for deleting all hidden files but not '.' 
nor '..'.

From: Eric Blake <address@hidden>
Date: Thu, 9 Apr 2009 11:13:51 -0600
Subject: [PATCH] Avoid problems caused by deleting in-use directory.

* lib/autotest/general.m4 (AT_INIT) <at_fn_group_prepare>: Only
remove the contents of $at_group_dir, not the directory itself.

Signed-off-by: Eric Blake <address@hidden>
 ChangeLog               |    4 ++++
 lib/autotest/general.m4 |    9 ++++++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 65c0250..fcbc835 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2009-04-09  Eric Blake  <address@hidden>

+       Avoid problems caused by deleting in-use directory.
+       * lib/autotest/general.m4 (AT_INIT) <at_fn_group_prepare>: Only
+       remove the contents of $at_group_dir, not the directory itself.
        Fix regression in empty test.
        * lib/autotest/general.m4 (AT_SETUP): Prep AT_ingroup for fallback
        use in empty test.  Fixes regression introduced 2009-04-06.
diff --git a/lib/autotest/general.m4 b/lib/autotest/general.m4
index b00c79b..9c6538e 100644
--- a/lib/autotest/general.m4
+++ b/lib/autotest/general.m4
@@ -999,8 +999,7 @@ m4_divert_pop([PREPARE_TESTS])dnl

 # Create the master directory if it doesn't already exist.
-test -d "$at_suite_dir" ||
-  mkdir "$at_suite_dir" ||
+AS_MKDIR_P(["$at_suite_dir"]) ||
   AS_ERROR([cannot create `$at_suite_dir'])

 # Can we diff with `/dev/null'?  DU 5.0 refuses.
@@ -1094,11 +1093,15 @@ at_fn_group_prepare ()

   # Create a fresh directory for the next test group, and enter.
+  # If one already exists, the user may have invoked ./run from
+  # within that directory; we remove the contents, but not the
+  # directory itself, so that we aren't pulling the rug out from
+  # under the shell's notion of the current directory.
   if test -d "$at_group_dir"; then
     find "$at_group_dir" -type d ! -perm -700 -exec chmod u+rwx \{\} \;
-    rm -fr "$at_group_dir" ||
+    rm -fr "$at_group_dir"/* "$at_group_dir"/.[!.] "$at_group_dir"/.??* ||
     AS_WARN([test directory for $at_group_normalized could not be cleaned.])
   # Be tolerant if the above `rm' was not able to remove the directory.

reply via email to

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