git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Fix merge-recursive on cygwin: broken errno when unlinking a directory
@ 2007-04-18 22:33 Alex Riesen
  2007-04-18 23:04 ` Linus Torvalds
  0 siblings, 1 reply; 6+ messages in thread
From: Alex Riesen @ 2007-04-18 22:33 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Looks like this time it is not cygwin, the you-know-what actually does
return a permission error.

Signed-off-by: Alex Riesen <raa.lkml@gmail.com>
---

I am very tempted to conditionally #define gitunlink
to say something rude about win32 and delete c:\boot.ini
instead. It will even work, in most setups.

 merge-recursive.c |    9 ++++++---
 1 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/merge-recursive.c b/merge-recursive.c
index 595b022..ae4032b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -610,16 +610,19 @@ static void update_file_flags(const unsigned char *sha,
 				die(msg, path, "");
 			}
 			if (unlink(path)) {
-				if (errno == EISDIR) {
+				struct stat st;
+				int err = errno;
+				if (err == EISDIR ||
+				    (err == EPERM && !lstat(path, &st) && S_ISDIR(st.st_mode))) {
 					/* something else exists */
 					error(msg, path, ": perhaps a D/F conflict?");
 					update_wd = 0;
 					goto update_index;
 				}
-				if (errno != ENOENT)
+				if (err != ENOENT)
 					die("failed to unlink %s "
 					    "in preparation to update: %s",
-					    path, strerror(errno));
+					    path, strerror(err));
 			}
 			if (mode & 0100)
 				mode = 0777;
-- 
1.5.1.1.876.ge36f76

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] Fix merge-recursive on cygwin: broken errno when unlinking a directory
  2007-04-18 22:33 [PATCH] Fix merge-recursive on cygwin: broken errno when unlinking a directory Alex Riesen
@ 2007-04-18 23:04 ` Linus Torvalds
  2007-04-18 23:40   ` Alex Riesen
  0 siblings, 1 reply; 6+ messages in thread
From: Linus Torvalds @ 2007-04-18 23:04 UTC (permalink / raw)
  To: Alex Riesen; +Cc: git, Junio C Hamano



On Thu, 19 Apr 2007, Alex Riesen wrote:
>
> +				struct stat st;
> +				int err = errno;
> +				if (err == EISDIR ||
> +				    (err == EPERM && !lstat(path, &st) && S_ISDIR(st.st_mode))) {

Can I ask people to please *not* write things like this?

Here's an important rule from Linus:

	Rule#1 when fixing bugs: there's a reason for the bug. And 
	_usually_ the reason was that the source code was hard to think 
	about or read.

	So you should always make the source code more readable when you 
	fix a bug. If you don't, the bug will just reappear or is just 
	more subtly hidden! A bugfix that just makes the same code even
	*harder* to read is not a fix at all!

(Side note: EPERM is actually apparently the POSIXLY correct error!)

Comlex conditionals are really just asking for bugs - either because they 
are buggy themselves, or because people don't understand what they really 
test for, and introduce bugs later.

That whole sequence should probably be a function of its own. But I'd also 
like to note that for some strange and inexplicable reason, the regular 
file handling and the symlink handling uses totally different setup, and 
the symlink handling does *not* do any of the error checks that the file 
case does.

So that function should be *common* to the two cases, and do the mkdir_p 
_and_ the unlink. As it is, I suspect we have some test-case for regular 
files that caused us to be careful with them, but we lack a test-case for 
symlinks, so we never bothered to make the code work either!

So here's a suggested and totally untested patch. It makes the code more 
readable, and probably fixes *two* bugs in the process. It also simply 
doesn't really even care what the error actually was - the important part 
was not that it was a directory, but that the unlink didn't succeed!

But even if somebody wants to re-introduce more errno value testing, I 
think you should apply this patch *first*, because source code readability 
really is very important!

Functions should be small and not have indentation of more than two 
levels.

[ And yes, I realize I don't always follow my own rules, but I try. I 
  often don't write a hell of a lot of comments when I write the first 
  version of the code - I tend to add them later when some piece of code 
  has been shown to need them - but I try very hard to make functions 
  small and simple and do *one* thing, and not have lots of levels of 
  indentation. And if you see me write bad code, please flame me too! ]

		Linus

---
 merge-recursive.c |   54 ++++++++++++++++++++++++++++------------------------
 1 files changed, 29 insertions(+), 25 deletions(-)

diff --git a/merge-recursive.c b/merge-recursive.c
index 595b022..cea6c87 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -574,6 +574,31 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
 	}
 }
 
+static int make_room_for_path(const char *path)
+{
+	int status;
+	const char *msg = "failed to create path '%s'%s";
+
+	status = mkdir_p(path, 0777);
+	if (status) {
+		if (status == -3) {
+			/* something else exists */
+			error(msg, path, ": perhaps a D/F conflict?");
+			return -1;
+		}
+		die(msg, path, "");
+	}
+
+	/* Successful unlink is good.. */
+	if (!unlink(path))
+		return 0;
+	/* .. and so is no existing file */
+	if (errno == ENOENT)
+		return 0;
+	/* .. but not some other error (who really cares what?) */
+	return error(msg, path, ": perhaps a D/F conflict?");
+}
+
 static void update_file_flags(const unsigned char *sha,
 			      unsigned mode,
 			      const char *path,
@@ -594,33 +619,12 @@ static void update_file_flags(const unsigned char *sha,
 		if (type != OBJ_BLOB)
 			die("blob expected for %s '%s'", sha1_to_hex(sha), path);
 
+		if (make_room_for_path(path) < 0) {
+			update_wd = 0;
+			goto update_index;
+		}
 		if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
 			int fd;
-			int status;
-			const char *msg = "failed to create path '%s'%s";
-
-			status = mkdir_p(path, 0777);
-			if (status) {
-				if (status == -3) {
-					/* something else exists */
-					error(msg, path, ": perhaps a D/F conflict?");
-					update_wd = 0;
-					goto update_index;
-				}
-				die(msg, path, "");
-			}
-			if (unlink(path)) {
-				if (errno == EISDIR) {
-					/* something else exists */
-					error(msg, path, ": perhaps a D/F conflict?");
-					update_wd = 0;
-					goto update_index;
-				}
-				if (errno != ENOENT)
-					die("failed to unlink %s "
-					    "in preparation to update: %s",
-					    path, strerror(errno));
-			}
 			if (mode & 0100)
 				mode = 0777;
 			else

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] Fix merge-recursive on cygwin: broken errno when unlinking a directory
  2007-04-18 23:04 ` Linus Torvalds
@ 2007-04-18 23:40   ` Alex Riesen
  2007-04-19  8:28     ` Alex Riesen
  0 siblings, 1 reply; 6+ messages in thread
From: Alex Riesen @ 2007-04-18 23:40 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git, Junio C Hamano

Linus Torvalds, Thu, Apr 19, 2007 01:04:06 +0200:
> >
> > +				struct stat st;
> > +				int err = errno;
> > +				if (err == EISDIR ||
> > +				    (err == EPERM && !lstat(path, &st) && S_ISDIR(st.st_mode))) {
> 
> Can I ask people to please *not* write things like this?
> 

Err... ok.

> 
> (Side note: EPERM is actually apparently the POSIXLY correct error!)
> 

Indeed it is 8-[]

> 
> So here's a suggested and totally untested patch. It makes the code more 
> readable, and probably fixes *two* bugs in the process. It also simply 
> doesn't really even care what the error actually was - the important part 
> was not that it was a directory, but that the unlink didn't succeed!
>

Well, it is a bit tested now. I'll repeat the testing tomorrow on that
windows box.

> +	/* .. but not some other error (who really cares what?) */
> +	return error(msg, path, ": perhaps a D/F conflict?");

I have to care sometimes when cygwin breaks where you never expect it
to. These annoying strerror(errno)'s a very helpful. IOW, how can
the user respond to the message which just tells "maybe it is
expected and you can fix it. Perhaps"? What do I do here next?
(well, I know what to do, but someone wont).

An lstat + S_ISDIR would at least make it plain "D/F conflict".

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Fix merge-recursive on cygwin: broken errno when unlinking a directory
  2007-04-18 23:40   ` Alex Riesen
@ 2007-04-19  8:28     ` Alex Riesen
  2007-04-19 16:39       ` Linus Torvalds
  0 siblings, 1 reply; 6+ messages in thread
From: Alex Riesen @ 2007-04-19  8:28 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git, Junio C Hamano

On 4/19/07, Alex Riesen <raa.lkml@gmail.com> wrote:
> >
> > So here's a suggested and totally untested patch. It makes the code more
> > readable, and probably fixes *two* bugs in the process. It also simply
> > doesn't really even care what the error actually was - the important part
> > was not that it was a directory, but that the unlink didn't succeed!
> >
>
> Well, it is a bit tested now. I'll repeat the testing tomorrow on that
> windows box.
>

Tested on windows too. Works.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] Fix merge-recursive on cygwin: broken errno when unlinking a directory
  2007-04-19  8:28     ` Alex Riesen
@ 2007-04-19 16:39       ` Linus Torvalds
  2007-04-19 18:31         ` Sam Ravnborg
  0 siblings, 1 reply; 6+ messages in thread
From: Linus Torvalds @ 2007-04-19 16:39 UTC (permalink / raw)
  To: Alex Riesen; +Cc: git, Junio C Hamano



On Thu, 19 Apr 2007, Alex Riesen wrote:

> On 4/19/07, Alex Riesen <raa.lkml@gmail.com> wrote:
> > >
> > > So here's a suggested and totally untested patch. It makes the code more
> > > readable, and probably fixes *two* bugs in the process. It also simply
> > > doesn't really even care what the error actually was - the important part
> > > was not that it was a directory, but that the unlink didn't succeed!
> > >
> > 
> > Well, it is a bit tested now. I'll repeat the testing tomorrow on that
> > windows box.
> 
> Tested on windows too. Works.

Good. Junio, I'd really suggest applying it. The old code was literally 
wrong, and depended on an error return that seems to be Linux-specific. It 
was also pretty ugly.

Here's the patch again with proper sign-off and a commentary..

As mentioned, maybe this wants expanding in the future, but regardless, 
the patch not only fixes a git problem on windows (and quite possibly 
other unixes too), any extensions will be much easier on top of this.

		Linus

---
From: Linus Torvalds <torvalds@linux-foundation.org>
Subject: Fix working directory errno handling when unlinking a directory

Alex Riesen noticed that the case where a file replaced a directory entry 
in the working tree was broken on cygwin. It turns out that the code made 
some Linux-specific assumptions, and also ignored errors entirely for the 
case where the entry was a symlink rather than a file.

This cleans it up by separating out the common case into a function of its 
own, so that both regular files and symlinks can share it, and by making 
the error handling more obvious (and not depend on any Linux-specific 
behaviour).

Acked-by: Alex Riesen <raa.lkml@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

---
 merge-recursive.c |   54 ++++++++++++++++++++++++++++------------------------
 1 files changed, 29 insertions(+), 25 deletions(-)

diff --git a/merge-recursive.c b/merge-recursive.c
index 595b022..cea6c87 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -574,6 +574,31 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
 	}
 }
 
+static int make_room_for_path(const char *path)
+{
+	int status;
+	const char *msg = "failed to create path '%s'%s";
+
+	status = mkdir_p(path, 0777);
+	if (status) {
+		if (status == -3) {
+			/* something else exists */
+			error(msg, path, ": perhaps a D/F conflict?");
+			return -1;
+		}
+		die(msg, path, "");
+	}
+
+	/* Successful unlink is good.. */
+	if (!unlink(path))
+		return 0;
+	/* .. and so is no existing file */
+	if (errno == ENOENT)
+		return 0;
+	/* .. but not some other error (who really cares what?) */
+	return error(msg, path, ": perhaps a D/F conflict?");
+}
+
 static void update_file_flags(const unsigned char *sha,
 			      unsigned mode,
 			      const char *path,
@@ -594,33 +619,12 @@ static void update_file_flags(const unsigned char *sha,
 		if (type != OBJ_BLOB)
 			die("blob expected for %s '%s'", sha1_to_hex(sha), path);
 
+		if (make_room_for_path(path) < 0) {
+			update_wd = 0;
+			goto update_index;
+		}
 		if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
 			int fd;
-			int status;
-			const char *msg = "failed to create path '%s'%s";
-
-			status = mkdir_p(path, 0777);
-			if (status) {
-				if (status == -3) {
-					/* something else exists */
-					error(msg, path, ": perhaps a D/F conflict?");
-					update_wd = 0;
-					goto update_index;
-				}
-				die(msg, path, "");
-			}
-			if (unlink(path)) {
-				if (errno == EISDIR) {
-					/* something else exists */
-					error(msg, path, ": perhaps a D/F conflict?");
-					update_wd = 0;
-					goto update_index;
-				}
-				if (errno != ENOENT)
-					die("failed to unlink %s "
-					    "in preparation to update: %s",
-					    path, strerror(errno));
-			}
 			if (mode & 0100)
 				mode = 0777;
 			else

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] Fix merge-recursive on cygwin: broken errno when unlinking a directory
  2007-04-19 16:39       ` Linus Torvalds
@ 2007-04-19 18:31         ` Sam Ravnborg
  0 siblings, 0 replies; 6+ messages in thread
From: Sam Ravnborg @ 2007-04-19 18:31 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Alex Riesen, git, Junio C Hamano

> +		if (status == -3) {
> +			/* something else exists */
> +			error(msg, path, ": perhaps a D/F conflict?");

It would be more helpful it you spelled it out like:
Directory/File conflict?


> +			return -1;
> +		}
> +		die(msg, path, "");
> +	}
> +
> +	/* Successful unlink is good.. */
> +	if (!unlink(path))
> +		return 0;
> +	/* .. and so is no existing file */
> +	if (errno == ENOENT)
> +		return 0;
> +	/* .. but not some other error (who really cares what?) */
> +	return error(msg, path, ": perhaps a D/F conflict?");
ditto.

I know this was how it looked previously too but thats just a lame excuse.

	Sam

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2007-04-19 18:30 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-04-18 22:33 [PATCH] Fix merge-recursive on cygwin: broken errno when unlinking a directory Alex Riesen
2007-04-18 23:04 ` Linus Torvalds
2007-04-18 23:40   ` Alex Riesen
2007-04-19  8:28     ` Alex Riesen
2007-04-19 16:39       ` Linus Torvalds
2007-04-19 18:31         ` Sam Ravnborg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).