Subject: Re: mkdir -p and EROFS
From: Paul Eggert <[email-masked]>
To: [email-masked] (Eric Blake)
Cc: [email-masked], [email-masked]
Date: Wed, 12 Oct 2005 14:24:09 -0700

[email-masked] (Eric Blake) writes:

> The algorithm change between 5.3.0 and 5.90 in lib/mkdir-p.c to
> try mkdir() first instead of stat(), and key off of EEXIST, breaks
> when mkdir() fails with EROFS on an intermediate directory when
> the writable directory has been mounted inside a read-only tree.

Thanks for reporting this.  It is indeed a bug in
coreutils/lib/mkdir-p.c.  It can be triggered by other errors too.
This is hard to write a test case for, but I'd like to fix things.
Does the following patch work for you?

Jim, if this works for Eric, is it OK to install this patch at this late
date?

2005-10-12  Paul Eggert  <[email-masked]>

	* mkdir-p.c (make_dir_parents): Don't fail if an intervening mkdir
	fails due to EROFS, or due to EEXIST or other reasons for that matter.
	Problem reported by Eric Blake.
	(ENOSYS): Remove; no longer needed.

--- mkdir-p.c.~1.11.~	2005-09-21 22:42:26.000000000 -0700
+++ mkdir-p.c	2005-10-12 14:21:22.000000000 -0700
@@ -45,10 +45,6 @@
 #include "quote.h"
 #include "stat-macros.h"
 
-#ifndef ENOSYS
-# define ENOSYS EEXIST
-#endif
-
 #define WX_USR (S_IWUSR | S_IXUSR)
 
 /* Ensure that the directory ARG exists.
@@ -175,6 +171,9 @@ make_dir_parents (char const *arg,
 
       while (true)
 	{
+	  bool dir_known_to_exist;
+	  int mkdir_errno;
+
 	  /* slash points to the leftmost unprocessed component of dir.  */
 	  basename_dir = slash;
 
@@ -188,7 +187,10 @@ make_dir_parents (char const *arg,
 	    basename_dir = dir;
 
 	  *slash = '\0';
-	  if (mkdir (basename_dir, tmp_mode) == 0)
+	  dir_known_to_exist = (mkdir (basename_dir, tmp_mode) == 0);
+	  mkdir_errno = errno;
+
+	  if (dir_known_to_exist)
 	    {
 	      if (verbose_fmt_string)
 		error (0, 0, verbose_fmt_string, quote (dir));
@@ -215,29 +217,30 @@ make_dir_parents (char const *arg,
 		  leading_dirs = new;
 		}
 	    }
-	  else if (errno == EEXIST || errno == ENOSYS)
-	    {
-	      /* A file is already there.  Perhaps it is a directory.
-		 If not, it will be diagnosed later.
-
-		 The ENOSYS is for Solaris 8 NFS clients, which can
-		 fail with errno == ENOSYS if mkdir is invoked on an
-		 NFS mount point.  */
-	    }
-	  else
-	    {
-	      error (0, errno, _("cannot create directory %s"), quote (dir));
-	      retval = false;
-	      break;
-	    }
 
 	  /* If we were able to save the initial working directory,
 	     then we can use chdir to change into each directory before
 	     creating an entry in that directory.  This avoids making
 	     mkdir process O(n^2) file name components.  */
-	  if (do_chdir && chdir (basename_dir) < 0)
+	  if (do_chdir)
+	    {
+	      if (chdir (basename_dir) == 0)
+		dir_known_to_exist = true;
+	      else if (dir_known_to_exist)
+		{
+		  error (0, errno, _("cannot chdir to directory %s"),
+			 quote (dir));
+		  retval = false;
+		  break;
+		}
+	    }
+	  else if (!dir_known_to_exist)
+	    dir_known_to_exist = (stat (basename_dir, &stats) == 0
+				  && S_ISDIR (stats.st_mode));
+
+	  if (!dir_known_to_exist)
 	    {
-	      error (0, errno, _("cannot chdir to directory %s"),
+	      error (0, mkdir_errno, _("cannot create directory %s"),
 		     quote (dir));
 	      retval = false;
 	      break;

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/