Thanks for reporting that inaccurate diagnostic. Sorry about the tricky code; it's a tricky situation. Looks like Collin's patch is not quite right.

I installed the attached patches into Gnulib. The first one fixes the two cases you mentioned, the second an unlikely bug I discovered in the neighborhood. Please give the patches a try. As they fix the bugs for me I am boldly closing the Coreutils bug report; we can reopen it if I'm wrong.
From 27db579667399d9f2cae2552a6f9185ffd10ab23 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Tue, 22 Jul 2025 12:09:28 -0700
Subject: [PATCH 1/2] mkdir-p: better diagnostics
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem reported by Lauri Tirkkonen <https://bugs.gnu.org/79072>.
* lib/mkdir-p.c (make_dir_parents):
If savewd_chdir fails due to anything other than EACCES, do
not attempt to preserve permissions; instead, fail with mkdir’s
errno if nonzero, and with savewd_chdir’s errno otherwise.
---
 ChangeLog     | 9 +++++++++
 lib/mkdir-p.c | 9 ++++++++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index 99ca126183..4841447b88 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2025-07-22  Paul Eggert  <egg...@cs.ucla.edu>
+
+	mkdir-p: better diagnostics
+	Problem reported by Lauri Tirkkonen <https://bugs.gnu.org/79072>.
+	* lib/mkdir-p.c (make_dir_parents):
+	If savewd_chdir fails due to anything other than EACCES, do
+	not attempt to preserve permissions; instead, fail with mkdir’s
+	errno if nonzero, and with savewd_chdir’s errno otherwise.
+
 2025-07-21  Collin Funk  <collin.fu...@gmail.com>
 
 	sys_un-h: Make sure that the 'sys' subdirectory is created.
diff --git a/lib/mkdir-p.c b/lib/mkdir-p.c
index f5df9843e4..fffa58b4eb 100644
--- a/lib/mkdir-p.c
+++ b/lib/mkdir-p.c
@@ -172,7 +172,7 @@ make_dir_parents (char *dir,
                               savewd_chdir_options, open_result);
               if (chdir_result < -1)
                 return true;
-              else
+              else if (chdir_result == 0 || errno == EACCES)
                 {
                   bool chdir_ok = (chdir_result == 0);
                   char const *subdir = (chdir_ok ? "." : dir + prefix_len);
@@ -193,6 +193,13 @@ make_dir_parents (char *dir,
                       return false;
                     }
                 }
+              else
+                {
+                  if (mkdir_errno == 0)
+                    mkdir_errno = errno;
+                  if (0 <= open_result[0])
+                    close (open_result[0]);
+                }
             }
         }
     }
-- 
2.48.1

From 84ddfc7bd29853ed91cdd65c7ce818072959f974 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Tue, 22 Jul 2025 12:12:22 -0700
Subject: [PATCH 2/2] mkdir-p: ENOENT/ENOTDIR safety and consistency

* lib/mkdir-p.c (make_dir_parents): If mkdir fails with ENOENT or
ENOTDIR, do not attempt anything else since the file does not exist.
Treat ENOENT and ENOTDIR consistently later, too.
---
 ChangeLog     | 5 +++++
 lib/mkdir-p.c | 9 +++++----
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 4841447b88..ac20d475da 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2025-07-22  Paul Eggert  <egg...@cs.ucla.edu>
 
+	mkdir-p: ENOENT/ENOTDIR safety and consistency
+	* lib/mkdir-p.c (make_dir_parents): If mkdir fails with ENOENT or
+	ENOTDIR, do not attempt anything else since the file does not exist.
+	Treat ENOENT and ENOTDIR consistently later, too.
+
 	mkdir-p: better diagnostics
 	Problem reported by Lauri Tirkkonen <https://bugs.gnu.org/79072>.
 	* lib/mkdir-p.c (make_dir_parents):
diff --git a/lib/mkdir-p.c b/lib/mkdir-p.c
index fffa58b4eb..7b764f122d 100644
--- a/lib/mkdir-p.c
+++ b/lib/mkdir-p.c
@@ -144,11 +144,13 @@ make_dir_parents (char *dir,
               mkdir_mode = -1;
             }
 
-          if (preserve_existing)
+          if (mkdir_errno == ENOENT || mkdir_errno == ENOTDIR)
+            ;
+          else if (preserve_existing)
             {
               if (mkdir_errno == 0)
                 return true;
-              if (mkdir_errno != ENOENT && make_ancestor)
+              if (make_ancestor)
                 {
                   struct stat st;
                   if (stat (dir + prefix_len, &st) == 0)
@@ -182,8 +184,7 @@ make_dir_parents (char *dir,
                     return true;
 
                   if (mkdir_errno == 0
-                      || (mkdir_errno != ENOENT && make_ancestor
-                          && errno != ENOTDIR))
+                      || (make_ancestor && errno != ENOENT && errno != ENOTDIR))
                     {
                       error (0, errno,
                              _(keep_owner
-- 
2.48.1

Reply via email to