autoconf: autom4te: correct error message when we can’t create autom4te.cache. (61648)

From 616480956de06e32526775c8b7f8fd34d04238a5 Mon Sep 17 00:00:00 2001
From: Zack Weinberg <[EMAIL REDACTED]>
Date: Mon, 21 Dec 2020 15:29:32 -0500
Subject: [PATCH] =?UTF-8?q?autom4te:=20correct=20error=20message=20when=20?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

While testing something else, I noticed that autom4te may print a
nonsensical error message when it fails to create autom4te.cache,
because it checks again whether the directory already exists before
giving up, and this clobbers errno.

Instead of doing (the perl equivalent of)
    test -d $cache || mkdir $cache || test -d $cache
call mkdir unconditionally.  If it fails with an errno code other than
EEXIST, consider that a hard failure; if it fails with EEXIST, check
whether the thing that exists is in fact a directory.  (A symlink to
a directory qualifies; I wouldn’t be surprised if people are moving
autom4te.cache around with symlinks.)

Either way, if we fail, report strerror(errno) from the original
mkdir failure.  Also, print the current working directory as part
of the error message; this aids debugging when you’re working with a
big hairy nested tree.

* bin/ Don’t check whether autom4te.cache exists before
  attempting to create it.  Only stat autom4te.cache if mkdir fails
  with EEXIST, otherwise fail immediately.  Make sure to report the
  errno code from mkdir, not the subsequent stat (if any).  Report
  the current working directory as part of the error message.

* tests/ Verify that autom4te reports the actual reason when
  it fails to create autom4te.cache.  Verify that failure to create
  autom4te.cache because that name exists, but isn’t a directory,
  is detected.
 bin/ | 19 ++++++++++++++-----
 tests/  | 20 ++++++++++++++++++--
 2 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/bin/ b/bin/
index febcdeea..4c2b905c 100644
--- a/bin/
+++ b/bin/
@@ -1012,12 +1012,21 @@ if ($freeze)
     exit $exit_code;
-# We need our cache directory.  Don't fail with parallel creation.
-if (! -d "$cache")
+# Ensure the cache directory exists.
+if (! mkdir ($cache, 0755))
-    mkdir "$cache", 0755
-      or -d "$cache"
-      or fatal "cannot create $cache: $!";
+    # Snapshot $! immediately, the next few operations may clobber it.
+    my $eexist = $!{EEXIST};
+    my $errmsg = "$!";
+    # If mkdir failed with EEXIST, that means the *name* $cache
+    # already exists, but it might be the wrong kind of file.
+    if (! $eexist || ! -d $cache)
+      {
+        require Cwd;
+        my $cwd = Cwd::cwd();
+        fatal "cannot create $cache in $cwd: $errmsg";
+      }
 # Open the index for update, and lock it.  autom4te handles several
diff --git a/tests/ b/tests/
index d32ad362..49710202 100644
--- a/tests/
+++ b/tests/
@@ -1534,20 +1534,36 @@ end-language: "Autoconf-without-aclocal-m4"
 # A failed redirection may cause a status of 2 with FreeBSD sh.
 AT_CHECK([(: > sub/some-file) || exit 1 && exit 77], 1, [ignore], [ignore])
-# Failure to create cache directory.
+# Failure to create cache directory due to access permissions.
 AT_CHECK_AUTOCONF([], [1], [ignore], [stderr])
 AT_CHECK([grep 'cannot create .*autom4te.cache' stderr], [0], [ignore])
+AT_CHECK([grep ': Permission denied' stderr], [0], [ignore])
 AT_CHECK([test -f configure], [1])
+# Failure to create cache directory due to something else in the way.
 chmod u+w sub
+: > sub/autom4te.cache
+AT_CHECK_AUTOCONF([], [1], [ignore], [stderr])
+AT_CHECK([grep 'cannot create .*autom4te.cache' stderr], [0], [ignore])
+AT_CHECK([grep ': File exists' stderr], [0], [ignore])
+AT_CHECK([test -f configure], [1])
+# This time, creation should succeed.
+rm -f sub/autom4te.cache
+AT_CHECK([test -d sub/autom4te.cache])
 rm -f configure sub/autom4te.cache/*
 chmod a-w sub/autom4te.cache
 # Failure to create a file in the cache directory.
 AT_CHECK_AUTOCONF([], [1], [ignore], [stderr])
-AT_CHECK([grep 'cannot open.*autom4te.cache' stderr], [0], [ignore])
+AT_CHECK([grep 'cannot open .*autom4te.cache' stderr], [0], [ignore])
+AT_CHECK([test -f configure], [1])
+# If the directory already exists, that should be fine.
+chmod u+w sub/autom4te.cache