git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Be more careful about updating refs
@ 2008-01-15 23:50 Linus Torvalds
  2008-01-16  0:02 ` Linus Torvalds
                   ` (3 more replies)
  0 siblings, 4 replies; 36+ messages in thread
From: Linus Torvalds @ 2008-01-15 23:50 UTC (permalink / raw)
  To: Git Mailing List, Junio C Hamano


This makes write_ref_sha1() more careful: it actually checks the SHA1 of 
the ref it is updating, and refuses to update a ref with an object that it 
cannot find.

Perhaps more importantly, it also refuses to update a branch head with a 
non-commit object. I don't quite know *how* the stable series maintainers 
were able to corrupt their repository to have a HEAD that pointed to a tag 
rather than a commit object, but they did. Which results in a totally 
broken repository that cannot be cloned or committed on.

So make it harder for people to shoot themselves in the foot like that.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---

I'm signing off on this, but I hope people will double-check this: I 
didn't actually test it very much.

 refs.c |   19 +++++++++++++++++++
 1 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/refs.c b/refs.c
index 58f6d17..c3ffe03 100644
--- a/refs.c
+++ b/refs.c
@@ -1119,10 +1119,16 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
 	return 0;
 }
 
+static int is_branch(const char *refname)
+{
+	return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
+}
+
 int write_ref_sha1(struct ref_lock *lock,
 	const unsigned char *sha1, const char *logmsg)
 {
 	static char term = '\n';
+	struct object *o;
 
 	if (!lock)
 		return -1;
@@ -1130,6 +1136,19 @@ int write_ref_sha1(struct ref_lock *lock,
 		unlock_ref(lock);
 		return 0;
 	}
+	o = parse_object(sha1);
+	if (!o) {
+		error("Trying to write ref %s with nonexistant object %s",
+			lock->ref_name, sha1_to_hex(sha1));
+		unlock_ref(lock);
+		return -1;
+	}
+	if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
+		error("Trying to write non-commit object %s to branch %s",
+			sha1_to_hex(sha1), lock->ref_name);
+		unlock_ref(lock);
+		return -1;
+	}
 	if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
 	    write_in_full(lock->lock_fd, &term, 1) != 1
 		|| close(lock->lock_fd) < 0) {

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

* Re: Be more careful about updating refs
  2008-01-15 23:50 Be more careful about updating refs Linus Torvalds
@ 2008-01-16  0:02 ` Linus Torvalds
  2008-01-16 19:52   ` Junio C Hamano
  2008-01-16  0:29 ` Junio C Hamano
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 36+ messages in thread
From: Linus Torvalds @ 2008-01-16  0:02 UTC (permalink / raw)
  To: Git Mailing List, Junio C Hamano



On Tue, 15 Jan 2008, Linus Torvalds wrote:
> 
> This makes write_ref_sha1() more careful: it actually checks the SHA1 of 
> the ref it is updating, and refuses to update a ref with an object that it 
> cannot find.

Side note: this breaks some tests, because those tests do things like

	git update-ref refs/heads/master 1111111111111111111111111111111111111111 &&
		test 1111111111111111111111111111111111111111 = $(cat .git/refs/heads/master)

which obviously won't work. I didn't update the tests, because I'm an evil 
person who just finds it very onerous to touch tests, and I particularly 
hate tests that turn out to be wrong.

(Pet peeve on mine: people fixing assert()'s by changing the source-code, 
without ever asking themselves whether maybe the assert itself was the 
bug).

		Linus

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

* Re: Be more careful about updating refs
  2008-01-15 23:50 Be more careful about updating refs Linus Torvalds
  2008-01-16  0:02 ` Linus Torvalds
@ 2008-01-16  0:29 ` Junio C Hamano
  2008-01-16  0:42   ` Linus Torvalds
  2008-01-16  1:11 ` Junio C Hamano
  2008-01-23 22:53 ` Sam Vilain
  3 siblings, 1 reply; 36+ messages in thread
From: Junio C Hamano @ 2008-01-16  0:29 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git

Linus Torvalds <torvalds@linux-foundation.org> writes:

> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
> ---
>
> I'm signing off on this, but I hope people will double-check this: I 
> didn't actually test it very much.

Does "Signed-off-by:" line mean something different for you than
for other people these days?

I thought that the rule that applies to you (and only you) on
this list was that all patches from you are DCO certified and I
am free to forge your signature on them, and the other rule that
applies to everybody including you is that Signed-off-by: is
about DCO certification and not about anything else
(e.g. author's confidence level about the patch).

I am asking because you did not have S-o-b and did not say
anything about the other patch about commit object creation.
Although both patches looked sane to me.

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

* Re: Be more careful about updating refs
  2008-01-16  0:29 ` Junio C Hamano
@ 2008-01-16  0:42   ` Linus Torvalds
  0 siblings, 0 replies; 36+ messages in thread
From: Linus Torvalds @ 2008-01-16  0:42 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git



On Tue, 15 Jan 2008, Junio C Hamano wrote:
> 
> Does "Signed-off-by:" line mean something different for you than
> for other people these days?

I intentionally don't tend to sign off stuff that I just send out as trial 
balloons, not meant to "really" be applied.

Of course, then I *also* forget to sign off emails that I do intend to be 
applied, so that rule-of-thumb is pretty weak ;)

		Linus

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

* Re: Be more careful about updating refs
  2008-01-15 23:50 Be more careful about updating refs Linus Torvalds
  2008-01-16  0:02 ` Linus Torvalds
  2008-01-16  0:29 ` Junio C Hamano
@ 2008-01-16  1:11 ` Junio C Hamano
  2008-01-23 22:53 ` Sam Vilain
  3 siblings, 0 replies; 36+ messages in thread
From: Junio C Hamano @ 2008-01-16  1:11 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Linus Torvalds <torvalds@linux-foundation.org> writes:

> This makes write_ref_sha1() more careful: it actually checks the SHA1 of 
> the ref it is updating, and refuses to update a ref with an object that it 
> cannot find.
>
> Perhaps more importantly, it also refuses to update a branch head with a 
> non-commit object. I don't quite know *how* the stable series maintainers 
> were able to corrupt their repository to have a HEAD that pointed to a tag 
> rather than a commit object, but they did. Which results in a totally 
> broken repository that cannot be cloned or committed on.

Two questions and a comment:

 - Do we want to impose the same restriction on refs/remotes/?
   I think that is a logical thing to do.

 - What should the receive-pack and git-fetch do if they trigger
   the check in this codepath while updating the refs under the
   affected hierarchies?  Fail the push and fetch?

 - I think !strcmp(refname, "HEAD") is not quite a right check
   to do here.  In order to catch the detached head case, it
   must be checked, but at the same time if the head is not
   detached, it should look at where the symref points at
   (i.e. a symref HEAD that points outside refs/heads is an
   error, and we need to catch that).

> +static int is_branch(const char *refname)
> +{
> +	return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
> +}

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

* Re: Be more careful about updating refs
  2008-01-16  0:02 ` Linus Torvalds
@ 2008-01-16 19:52   ` Junio C Hamano
  2008-01-17  9:15     ` Charles Bailey
  0 siblings, 1 reply; 36+ messages in thread
From: Junio C Hamano @ 2008-01-16 19:52 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List

Linus Torvalds <torvalds@linux-foundation.org> writes:

> On Tue, 15 Jan 2008, Linus Torvalds wrote:
>> 
>> This makes write_ref_sha1() more careful: it actually checks the SHA1 of 
>> the ref it is updating, and refuses to update a ref with an object that it 
>> cannot find.
>
> Side note: this breaks some tests, because those tests do things like
>
> 	git update-ref refs/heads/master 1111111111111111111111111111111111111111 &&
> 		test 1111111111111111111111111111111111111111 = $(cat .git/refs/heads/master)
>
> ...
> (Pet peeve on mine: people fixing assert()'s by changing the source-code, 
> without ever asking themselves whether maybe the assert itself was the 
> bug).

The rules for the plumbing used to be that refs can point at
anything that get_sha1() accepts.  We did not even require it to
be parse_object() happy let alone it being parse_commit() kosher.

You changed the world order.  I agree that the world order was
changed in a good way, but saying that the original test did not
check the right thing or it was a bug is not quite fair.  At
worst, we can say that it was very sloppily written by assuming
that the commands involved in the particular test would not care
about corrupted repositories whose refs point at nonexistant
bogus objects.

I'll squash the following to your patch.

---
 t/t1400-update-ref.sh |   19 +++++++++++++------
 1 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index a90824b..71ab2dd 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -7,12 +7,19 @@ test_description='Test git update-ref and basic ref logging'
 . ./test-lib.sh
 
 Z=0000000000000000000000000000000000000000
-A=1111111111111111111111111111111111111111
-B=2222222222222222222222222222222222222222
-C=3333333333333333333333333333333333333333
-D=4444444444444444444444444444444444444444
-E=5555555555555555555555555555555555555555
-F=6666666666666666666666666666666666666666
+
+test_expect_success setup '
+
+	for name in A B C D E F
+	do
+		test_tick &&
+		T=$(git write-tree) &&
+		sha1=$(echo $name | git commit-tree $T) &&
+		eval $name=$sha1
+	done
+
+'
+
 m=refs/heads/master
 n_dir=refs/heads/gu
 n=$n_dir/fixes

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

* Re: Be more careful about updating refs
  2008-01-16 19:52   ` Junio C Hamano
@ 2008-01-17  9:15     ` Charles Bailey
  2008-01-17 10:52       ` Johannes Sixt
  2008-01-17 10:56       ` Charles Bailey
  0 siblings, 2 replies; 36+ messages in thread
From: Charles Bailey @ 2008-01-17  9:15 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, Git Mailing List

On Wed, Jan 16, 2008 at 11:52:43AM -0800, Junio C Hamano wrote:
> Linus Torvalds <torvalds@linux-foundation.org> writes:
> 
> > On Tue, 15 Jan 2008, Linus Torvalds wrote:
> >> 
> >> This makes write_ref_sha1() more careful: it actually checks the SHA1 of 
> >> the ref it is updating, and refuses to update a ref with an object that it 
> >> cannot find.
> >
> > Side note: this breaks some tests, because those tests do things like
> >
> > 	git update-ref refs/heads/master 1111111111111111111111111111111111111111 &&
> > 		test 1111111111111111111111111111111111111111 = $(cat .git/refs/heads/master)
> >
> > ...
> > (Pet peeve on mine: people fixing assert()'s by changing the source-code, 
> > without ever asking themselves whether maybe the assert itself was the 
> > bug).
> 
> The rules for the plumbing used to be that refs can point at
> anything that get_sha1() accepts.  We did not even require it to
> be parse_object() happy let alone it being parse_commit() kosher.
> 
> You changed the world order.  I agree that the world order was
> changed in a good way, but saying that the original test did not
> check the right thing or it was a bug is not quite fair.  At
> worst, we can say that it was very sloppily written by assuming
> that the commands involved in the particular test would not care
> about corrupted repositories whose refs point at nonexistant
> bogus objects.
> 
> I'll squash the following to your patch.

I'm assuming that this original patch and the test update turned into
the following commit in master:

c3b0dec509fe136c5417422f31898b5a4e2d5e02 is first bad commit

I just thought I should warn you that this seems (git bisect tells me
so) to have caused a failure in t9301-fast-export.sh on my Mac OS X
10.4.11 machine although I haven't yet had the time to investigate
why.

Charles.

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

* Re: Be more careful about updating refs
  2008-01-17  9:15     ` Charles Bailey
@ 2008-01-17 10:52       ` Johannes Sixt
  2008-01-17 11:01         ` Charles Bailey
  2008-01-17 10:56       ` Charles Bailey
  1 sibling, 1 reply; 36+ messages in thread
From: Johannes Sixt @ 2008-01-17 10:52 UTC (permalink / raw)
  To: Charles Bailey; +Cc: Junio C Hamano, Linus Torvalds, Git Mailing List

Charles Bailey schrieb:
> I'm assuming that this original patch and the test update turned into
> the following commit in master:
> 
> c3b0dec509fe136c5417422f31898b5a4e2d5e02 is first bad commit
> 
> I just thought I should warn you that this seems (git bisect tells me
> so) to have caused a failure in t9301-fast-export.sh on my Mac OS X
> 10.4.11 machine although I haven't yet had the time to investigate
> why.

I observed the same (on Windows). The reason is that above-mentioned
commit introduces a call to parse_objects(). But by the time that
fast-import calls write_ref_sha1() (and, hence, this new parse_objects())
it has not yet written a pack file, and parse_objects() fails. I don't
have a clue how to fix this short of reverting the commit.

-- Hannes

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

* Re: Be more careful about updating refs
  2008-01-17  9:15     ` Charles Bailey
  2008-01-17 10:52       ` Johannes Sixt
@ 2008-01-17 10:56       ` Charles Bailey
  1 sibling, 0 replies; 36+ messages in thread
From: Charles Bailey @ 2008-01-17 10:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, Git Mailing List

On Thu, Jan 17, 2008 at 09:15:58AM +0000, Charles Bailey wrote:
> 
> c3b0dec509fe136c5417422f31898b5a4e2d5e02 is first bad commit
> 
> I just thought I should warn you that this seems (git bisect tells me
> so) to have caused a failure in t9301-fast-export.sh on my Mac OS X
> 10.4.11 machine although I haven't yet had the time to investigate
> why.
> 

Further to this, the first problem is that the 'fast-export |
fast-import' test is failing.

As far as I can tell, fast-export is behaving no differently, the
generated export file looks identical in shape.  It seems that
fast-import is creating a bad temporary packfile for itself.

The error that I'm getting is:

fatal: unknown object type 0 in
.git/objects/pack/pack-6be2b92c2d7485fd5cefbb0d9a68827e4c23d548.pack
fast-import: dumping crash report to .git/fast_import_crash_12317

which seems to come from the unpack_entry function in sha1_file.c.

I haven't got much deeper into how the refs.c change has caused the
issue, or why on earth it seems to be a Mac only issue.

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

* Re: Be more careful about updating refs
  2008-01-17 10:52       ` Johannes Sixt
@ 2008-01-17 11:01         ` Charles Bailey
  2008-01-17 12:41           ` Johannes Sixt
  0 siblings, 1 reply; 36+ messages in thread
From: Charles Bailey @ 2008-01-17 11:01 UTC (permalink / raw)
  To: Johannes Sixt; +Cc: Junio C Hamano, Linus Torvalds, Git Mailing List

On Thu, Jan 17, 2008 at 11:52:23AM +0100, Johannes Sixt wrote:
> 
> I observed the same (on Windows). The reason is that above-mentioned
> commit introduces a call to parse_objects(). But by the time that
> fast-import calls write_ref_sha1() (and, hence, this new parse_objects())
> it has not yet written a pack file, and parse_objects() fails. I don't
> have a clue how to fix this short of reverting the commit.
> 

OK, so it's not just Mac OS X, then.  From your description and my
initial poke at the code,  I can't immediately see a good reason why
the test should succeed on Linux, though.

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

* Re: Be more careful about updating refs
  2008-01-17 11:01         ` Charles Bailey
@ 2008-01-17 12:41           ` Johannes Sixt
  2008-01-17 12:58             ` Johannes Schindelin
  2008-01-18  1:43             ` Junio C Hamano
  0 siblings, 2 replies; 36+ messages in thread
From: Johannes Sixt @ 2008-01-17 12:41 UTC (permalink / raw)
  To: Charles Bailey
  Cc: Junio C Hamano, Linus Torvalds, Git Mailing List, Shawn O. Pearce

Charles Bailey schrieb:
> On Thu, Jan 17, 2008 at 11:52:23AM +0100, Johannes Sixt wrote:
>> I observed the same (on Windows). The reason is that above-mentioned
>> commit introduces a call to parse_objects(). But by the time that
>> fast-import calls write_ref_sha1() (and, hence, this new parse_objects())
>> it has not yet written a pack file, and parse_objects() fails. I don't
>> have a clue how to fix this short of reverting the commit.
>>
> 
> OK, so it's not just Mac OS X, then.  From your description and my
> initial poke at the code,  I can't immediately see a good reason why
> the test should succeed on Linux, though.

My analysis is not correct. The pack file is present, but it seems to be
incomplete.

The reason is the NO_MMAP build flag. If you compile with
NO_MMAP=YesPlease on Linux, t9301 fails as well. Does this ring a bell?

-- Hannes

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

* Re: Be more careful about updating refs
  2008-01-17 12:41           ` Johannes Sixt
@ 2008-01-17 12:58             ` Johannes Schindelin
  2008-01-17 13:07               ` Charles Bailey
  2008-01-18  1:43             ` Junio C Hamano
  1 sibling, 1 reply; 36+ messages in thread
From: Johannes Schindelin @ 2008-01-17 12:58 UTC (permalink / raw)
  To: Johannes Sixt
  Cc: Charles Bailey, Junio C Hamano, Linus Torvalds, Git Mailing List,
	Shawn O. Pearce

Hi,

On Thu, 17 Jan 2008, Johannes Sixt wrote:

> Charles Bailey schrieb:
> > On Thu, Jan 17, 2008 at 11:52:23AM +0100, Johannes Sixt wrote:
> >> I observed the same (on Windows). The reason is that above-mentioned
> >> commit introduces a call to parse_objects(). But by the time that
> >> fast-import calls write_ref_sha1() (and, hence, this new parse_objects())
> >> it has not yet written a pack file, and parse_objects() fails. I don't
> >> have a clue how to fix this short of reverting the commit.
> >>
> > 
> > OK, so it's not just Mac OS X, then.  From your description and my
> > initial poke at the code,  I can't immediately see a good reason why
> > the test should succeed on Linux, though.
> 
> My analysis is not correct. The pack file is present, but it seems to be
> incomplete.
> 
> The reason is the NO_MMAP build flag. If you compile with
> NO_MMAP=YesPlease on Linux, t9301 fails as well. Does this ring a bell?

Just a wild guess... Could it be that the mmap() happened before contents 
were written to that fd?  The faked mmap() is just a malloc() && pread(), 
and would thus miss out on changes written after the mmap() call.

Ciao,
Dscho

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

* Re: Be more careful about updating refs
  2008-01-17 12:58             ` Johannes Schindelin
@ 2008-01-17 13:07               ` Charles Bailey
  0 siblings, 0 replies; 36+ messages in thread
From: Charles Bailey @ 2008-01-17 13:07 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Johannes Sixt, Junio C Hamano, Linus Torvalds, Git Mailing List,
	Shawn O. Pearce

On Thu, Jan 17, 2008 at 12:58:07PM +0000, Johannes Schindelin wrote:
> 
> Just a wild guess... Could it be that the mmap() happened before contents 
> were written to that fd?  The faked mmap() is just a malloc() && pread(), 
> and would thus miss out on changes written after the mmap() call.

This would be my guess too.  As far as I can tell fast-import is
creating objects directly in a packfile.  I'm guessing here, but the
new checking code is probably being triggered to check (for example)
the parent ref for commits that fast-import is creating and the check
only works if mmap can successfully examine objects in the
still-being-written packfile.

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

* Re: Be more careful about updating refs
  2008-01-17 12:41           ` Johannes Sixt
  2008-01-17 12:58             ` Johannes Schindelin
@ 2008-01-18  1:43             ` Junio C Hamano
  2008-01-18  2:01               ` Junio C Hamano
  2008-01-18  2:30               ` Be more careful about updating refs Shawn O. Pearce
  1 sibling, 2 replies; 36+ messages in thread
From: Junio C Hamano @ 2008-01-18  1:43 UTC (permalink / raw)
  To: Johannes Sixt, Shawn O. Pearce
  Cc: Charles Bailey, Linus Torvalds, Git Mailing List

Johannes Sixt <j.sixt@viscovery.net> writes:

> Charles Bailey schrieb:
>> On Thu, Jan 17, 2008 at 11:52:23AM +0100, Johannes Sixt wrote:
>>> I observed the same (on Windows). The reason is that above-mentioned
>>> commit introduces a call to parse_objects(). But by the time that
>>> fast-import calls write_ref_sha1() (and, hence, this new parse_objects())
>>> it has not yet written a pack file, and parse_objects() fails. I don't
>>> have a clue how to fix this short of reverting the commit.
>>>
>> 
>> OK, so it's not just Mac OS X, then.  From your description and my
>> initial poke at the code,  I can't immediately see a good reason why
>> the test should succeed on Linux, though.
>
> My analysis is not correct. The pack file is present, but it seems to be
> incomplete.
>
> The reason is the NO_MMAP build flag. If you compile with
> NO_MMAP=YesPlease on Linux, t9301 fails as well. Does this ring a bell?

I ran strace and found that fast-import retains three windows to
the same data that was opened while the pack was still being
built (i.e. the filename is still tmp_pack_XXXXXX) when it dies:

    {next = 0x6f6d20, base = 0x6f77a0 "PACK", offset = 0, len = 907,
     last_used = 9, inuse_cnt = 0}
    {next = 0x728630, base = 0x6f7160 "PACK", offset = 0, len = 500, 
      last_used = 5, inuse_cnt = 0}
    {next = 0x0, base = 0x6f6d50 "PACK", offset = 0, len = 261, 
      last_used = 1, inuse_cnt = 0}

When it hits end_packfile() to install the built-pack to its
final destination and makes it available to the rest of git,
however, it has this clever code to reuse the still open window.

The object offset asked when it dies is at offset 887.  However,
tmp_pack was written after that last window with length 907 has
been (re)opened, and reusing the window results in reading a
stale data.

The following patch seems to fix the issue for me, but this is
primarily meant for discussion, as I do not quite understand why
the same issue does not manifest itself when NO_MMAP is not
used.

diff --git a/fast-import.c b/fast-import.c
index 3609c24..bd0ddb1 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -926,7 +926,13 @@ static void end_packfile(void)
 		new_p = add_packed_git(idx_name, strlen(idx_name), 1);
 		if (!new_p)
 			die("core git rejected index %s", idx_name);
-		new_p->windows = old_p->windows;
+		while (old_p->windows) {
+			struct pack_window *w = old_p->windows;
+			munmap(w->base, w->len);
+			old_p->windows = w->next;
+			free(w);
+		}
+		new_p->windows = NULL;
 		all_packs[pack_id] = new_p;
 		install_packed_git(new_p);
 

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

* Re: Be more careful about updating refs
  2008-01-18  1:43             ` Junio C Hamano
@ 2008-01-18  2:01               ` Junio C Hamano
  2008-01-18  2:13                 ` Shawn O. Pearce
  2008-01-18  2:30               ` Be more careful about updating refs Shawn O. Pearce
  1 sibling, 1 reply; 36+ messages in thread
From: Junio C Hamano @ 2008-01-18  2:01 UTC (permalink / raw)
  To: Shawn O. Pearce, Johannes Sixt
  Cc: Charles Bailey, Linus Torvalds, Git Mailing List

Junio C Hamano <gitster@pobox.com> writes:

> The following patch seems to fix the issue for me, but this is
> primarily meant for discussion, as I do not quite understand why
> the same issue does not manifest itself when NO_MMAP is not
> used.
> ...

BTW, the lookup of the object that dies is in update_branch().
The call to write_ref_sha1() at the last tries to verify b->sha1
is available and is a commit.

        static int update_branch(struct branch *b)
        {
                static const char *msg = "fast-import";
                struct ref_lock *lock;
                unsigned char old_sha1[20];

                if (read_ref(b->name, old_sha1))
                        hashclr(old_sha1);
                lock = lock_any_ref_for_update(b->name, old_sha1, 0);
                if (!lock)
                        return error("Unable to lock %s", b->name);
                if (!force_update && !is_null_sha1(old_sha1)) {
                        struct commit *old_cmit, *new_cmit;

                        old_cmit = lookup_commit_reference_gently(old_sha1, 0);
                        new_cmit = lookup_commit_reference_gently(b->sha1, 0);
                        if (!old_cmit || !new_cmit) {
                                unlock_ref(lock);
                                return error("Branch %s is missing commits.", b->name);
                        }

                        if (!in_merge_bases(old_cmit, &new_cmit, 1)) {
                                unlock_ref(lock);
                                warning("Not updating %s"
                                        " (new tip %s does not contain %s)",
                                        b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
                                return -1;
                        }
                }
                if (write_ref_sha1(lock, b->sha1, msg) < 0)
                        return error("Unable to update %s", b->name);
                return 0;
        }

The write_ref_sha1() function previously did not even check if
the object pointed at by b->sha1 actually existed, but now it
parses the object and makes sure it is a commit, if the updated
ref is under ref/heads/ hierarchy.

What I do not quite understand is how this can be a new issue.

The codepath to allow updating an existing branch shown above
(i.e. "if it is not force and old is not NULL") uses the usual
lookup_commit_reference_gently() interface to access b->sha1,
and does not use gfi-aware gfi_unpack_entry() or anything
magical, which means it would have passed the same codepath down
to trigger the same issue.  IOW, even before this tightening of
write_ref_sha1(), we already should have had the issue of not
being to able to grab the object b->sha1 refers to out of the
newly built packfile.

Is it just nobody seriously exercised the codepath yet, or is
there a difference between these two calls that is more subtle
than that?

Quite confused...

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

* Re: Be more careful about updating refs
  2008-01-18  2:01               ` Junio C Hamano
@ 2008-01-18  2:13                 ` Shawn O. Pearce
  2008-01-18  2:25                   ` Junio C Hamano
  0 siblings, 1 reply; 36+ messages in thread
From: Shawn O. Pearce @ 2008-01-18  2:13 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Sixt, Charles Bailey, Linus Torvalds, Git Mailing List

Junio C Hamano <gitster@pobox.com> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
> > The following patch seems to fix the issue for me, but this is
> > primarily meant for discussion, as I do not quite understand why
> > the same issue does not manifest itself when NO_MMAP is not
> > used.
> > ...
...
> What I do not quite understand is how this can be a new issue.

It isn't.  It is indeed a pretty old issue, as far as git issues go.
Its probably about as old as fast-import being accepted into your
tree is.
 
> The codepath to allow updating an existing branch shown above
> (i.e. "if it is not force and old is not NULL") uses the usual
> lookup_commit_reference_gently() interface to access b->sha1,
> and does not use gfi-aware gfi_unpack_entry() or anything
> magical, which means it would have passed the same codepath down
> to trigger the same issue.  IOW, even before this tightening of
> write_ref_sha1(), we already should have had the issue of not
> being to able to grab the object b->sha1 refers to out of the
> newly built packfile.
> 
> Is it just nobody seriously exercised the codepath yet, or is
> there a difference between these two calls that is more subtle
> than that?

I think the problem is nobody has tested fast-import updating an
existing ref while using NO_MMAP.  Or if they did, they didn't report
the problem as they didn't figure they needed fast-import that badly.

Updating an existing ref is not a common operation, but the test
suite does test for it.  So it must be the NO_MMAP configuration
is simply not being tested well enough.

-- 
Shawn.

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

* Re: Be more careful about updating refs
  2008-01-18  2:13                 ` Shawn O. Pearce
@ 2008-01-18  2:25                   ` Junio C Hamano
  2008-01-18  2:33                     ` Shawn O. Pearce
  0 siblings, 1 reply; 36+ messages in thread
From: Junio C Hamano @ 2008-01-18  2:25 UTC (permalink / raw)
  To: Shawn O. Pearce
  Cc: Johannes Sixt, Charles Bailey, Linus Torvalds, Git Mailing List

"Shawn O. Pearce" <spearce@spearce.org> writes:

> I think the problem is nobody has tested fast-import updating an
> existing ref while using NO_MMAP.  Or if they did, they didn't report
> the problem as they didn't figure they needed fast-import that badly.
>
> Updating an existing ref is not a common operation, but the test
> suite does test for it.  So it must be the NO_MMAP configuration
> is simply not being tested well enough.

Ok, thanks.

Now a more important question is how we would properly fix this
issue?

I suspect that fast-import is the only one that opens windows
into an unfinalized pack, and if that is the case, it would be
the only program that may be hit by the issue of mmap emulation
getting stale data.

I do not think the patch I posted was correct at all.

Especially, I am not sure if the issue only exists at the
end_packfile() boundary.  Don't we have the same issue reading
from the packfile being built, and isn't the only reason my hack
works it around is because access patterns of the testsuite
happens to not trigger it?

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

* Re: Be more careful about updating refs
  2008-01-18  1:43             ` Junio C Hamano
  2008-01-18  2:01               ` Junio C Hamano
@ 2008-01-18  2:30               ` Shawn O. Pearce
  1 sibling, 0 replies; 36+ messages in thread
From: Shawn O. Pearce @ 2008-01-18  2:30 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Sixt, Charles Bailey, Linus Torvalds, Git Mailing List

Junio C Hamano <gitster@pobox.com> wrote:
> I ran strace and found that fast-import retains three windows to
> the same data that was opened while the pack was still being
> built (i.e. the filename is still tmp_pack_XXXXXX) when it dies:
> 
>     {next = 0x6f6d20, base = 0x6f77a0 "PACK", offset = 0, len = 907,
>      last_used = 9, inuse_cnt = 0}
>     {next = 0x728630, base = 0x6f7160 "PACK", offset = 0, len = 500, 
>       last_used = 5, inuse_cnt = 0}
>     {next = 0x0, base = 0x6f6d50 "PACK", offset = 0, len = 261, 
>       last_used = 1, inuse_cnt = 0}

Ouch.  Hmm, well, maybe it shouldn't keep three windows open to
the same part of the file, but with different lengths.  :)

But that's another issue.
 
> The following patch seems to fix the issue for me, but this is
> primarily meant for discussion,

I agree the patch should fix this particular issue and the
performance difference it will cause is minor enough to not be
worth discussing further.  But I have one minor comment (please
see it below).

> as I do not quite understand why
> the same issue does not manifest itself when NO_MMAP is not
> used.

When NO_MMAP is off (we are actually using the OS's true mmap)
the data is updated into the window when the file is written,
as the window is backed by the OS's filesystem cache.

If the data access is outside of the window (past the offset of
the longest window, here 907) we would just open a new window to
the relevant region of the file.  But when it is inside the window,
the window being backed by the filesystem cache saved us.  In the
case of NO_MMAP this doesn't work, so we get a failure later during
object access.

In particular if you look at gfi_unpack_entry() (which is where
we cause windows to be opened on the file being created) we tell
sha1_file.c it has 20 bytes available in the window *beyond* the
actual end of the file.  This is due to an assumption within the
windowing code of sha1_file.c that we always have the packfile
footer at the end of the file, so all windowing code assumes it
has at least 20 bytes from the start of a window that it can access
without needing to perform additional bounds checking.

So what's happening here is we are adding objects into the file,
some of which may have their data appearing in the trailing 20 bytes
of some prior window.  If that happens the cached window looks like
it can be used to access the data, but it really cannot be in the
NO_MMAP case as those trailing 20 bytes of the window are all \0's.

The astute reader may wonder how gfi_unpack_entry() works when
NO_MMAP is being used.  It doesn't for that last 20 bytes of
any window.  Which has me thinking that Junio's patch alone isn't
enough to make fast-import work correctly under NO_MMAP.

> diff --git a/fast-import.c b/fast-import.c
> index 3609c24..bd0ddb1 100644
> --- a/fast-import.c
> +++ b/fast-import.c
> @@ -926,7 +926,13 @@ static void end_packfile(void)
>  		new_p = add_packed_git(idx_name, strlen(idx_name), 1);
>  		if (!new_p)
>  			die("core git rejected index %s", idx_name);
> -		new_p->windows = old_p->windows;
> +		while (old_p->windows) {
> +			struct pack_window *w = old_p->windows;
> +			munmap(w->base, w->len);
> +			old_p->windows = w->next;
> +			free(w);
> +		}
> +		new_p->windows = NULL;

This assignment of NULL should not be necessary.  add_packed_git()
already does this for us on new_p so we do not need to repeat it
here in fast-import.

>  		all_packs[pack_id] = new_p;
>  		install_packed_git(new_p);
>  

-- 
Shawn.

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

* Re: Be more careful about updating refs
  2008-01-18  2:25                   ` Junio C Hamano
@ 2008-01-18  2:33                     ` Shawn O. Pearce
  2008-01-18  2:58                       ` Shawn O. Pearce
  0 siblings, 1 reply; 36+ messages in thread
From: Shawn O. Pearce @ 2008-01-18  2:33 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Sixt, Charles Bailey, Linus Torvalds, Git Mailing List

Junio C Hamano <gitster@pobox.com> wrote:
> "Shawn O. Pearce" <spearce@spearce.org> writes:
> 
> > I think the problem is nobody has tested fast-import updating an
> > existing ref while using NO_MMAP.  Or if they did, they didn't report
> > the problem as they didn't figure they needed fast-import that badly.
> >
> > Updating an existing ref is not a common operation, but the test
> > suite does test for it.  So it must be the NO_MMAP configuration
> > is simply not being tested well enough.
> 
> Now a more important question is how we would properly fix this
> issue?
> 
> I suspect that fast-import is the only one that opens windows
> into an unfinalized pack, and if that is the case, it would be
> the only program that may be hit by the issue of mmap emulation
> getting stale data.

Yes, it is the only program that is foolish enough to access a
partial packfile.  index-pack uses pread(), and only after it has
the entire packfile downloaded to the local system.  The only other
reader of a partial packfile is unpack-objects, and obviously that
doesn't care about seeking backwards within the packfile.

So its probably the only user who suffers from the mmap emulation.
 
> I do not think the patch I posted was correct at all.
> 
> Especially, I am not sure if the issue only exists at the
> end_packfile() boundary.  Don't we have the same issue reading
> from the packfile being built, and isn't the only reason my hack
> works it around is because access patterns of the testsuite
> happens to not trigger it?

Yes, that's my take on it as well (see my other email).  The
testsuite must just be really lucky that its not hitting the
boundary condition.

I almost said gfi_unpack_entry() was immune from this bug, but
I went back and read the code again and determined that it does
in fact suffer from this under NO_MMAP, and we're just really
damn lucky nobody has caused it.

I'll try to work up a patch this evening.

-- 
Shawn.

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

* Re: Be more careful about updating refs
  2008-01-18  2:33                     ` Shawn O. Pearce
@ 2008-01-18  2:58                       ` Shawn O. Pearce
  2008-01-18  3:18                         ` Shawn O. Pearce
  0 siblings, 1 reply; 36+ messages in thread
From: Shawn O. Pearce @ 2008-01-18  2:58 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Sixt, Charles Bailey, Linus Torvalds, Git Mailing List

"Shawn O. Pearce" <spearce@spearce.org> wrote:
> Junio C Hamano <gitster@pobox.com> wrote:
> > 
> > Especially, I am not sure if the issue only exists at the
> > end_packfile() boundary.  Don't we have the same issue reading
> > from the packfile being built, and isn't the only reason my hack
> > works it around is because access patterns of the testsuite
> > happens to not trigger it?
> 
> Yes, that's my take on it as well (see my other email).  The
> testsuite must just be really lucky that its not hitting the
> boundary condition.
> 
> I almost said gfi_unpack_entry() was immune from this bug, but
> I went back and read the code again and determined that it does
> in fact suffer from this under NO_MMAP, and we're just really
> damn lucky nobody has caused it.

I think this solves the problem.  Its based on your first patch, but
would replace it.  The trick here is we close the cached windows if
we are accessing data from the packfile we are appending into and we
have increased the file length.  This way we don't blow away windows
during high read/low write periods, like during branch cache reloads.

--8>--
diff --git a/fast-import.c b/fast-import.c
index 3609c24..8e7747c 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -907,6 +907,16 @@ static void unkeep_all_packs(void)
 	}
 }
 
+static void close_all_windows(struct packed_git *p)
+{
+	while (p->windows) {
+		struct pack_window *w = p->windows;
+		munmap(w->base, w->len);
+		p->windows = w->next;
+		free(w);
+	}
+}
+
 static void end_packfile(void)
 {
 	struct packed_git *old_p = pack_data, *new_p;
@@ -917,6 +927,7 @@ static void end_packfile(void)
 		struct branch *b;
 		struct tag *t;
 
+		close_all_windows(pack_data);
 		fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
 				    pack_data->pack_name, object_count);
 		close(pack_data->pack_fd);
@@ -926,7 +937,6 @@ static void end_packfile(void)
 		new_p = add_packed_git(idx_name, strlen(idx_name), 1);
 		if (!new_p)
 			die("core git rejected index %s", idx_name);
-		new_p->windows = old_p->windows;
 		all_packs[pack_id] = new_p;
 		install_packed_git(new_p);
 
@@ -1129,8 +1139,10 @@ static void *gfi_unpack_entry(
 {
 	enum object_type type;
 	struct packed_git *p = all_packs[oe->pack_id];
-	if (p == pack_data)
+	if (p == pack_data && p->pack_size < (pack_size + 20)) {
+		close_all_windows(p);
 		p->pack_size = pack_size + 20;
+	}
 	return unpack_entry(p, oe->offset, &type, sizep);
 }
 

-- 
Shawn.

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

* Re: Be more careful about updating refs
  2008-01-18  2:58                       ` Shawn O. Pearce
@ 2008-01-18  3:18                         ` Shawn O. Pearce
  2008-01-18  3:22                           ` Shawn O. Pearce
  0 siblings, 1 reply; 36+ messages in thread
From: Shawn O. Pearce @ 2008-01-18  3:18 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Sixt, Charles Bailey, Linus Torvalds, Git Mailing List

"Shawn O. Pearce" <spearce@spearce.org> wrote:
> "Shawn O. Pearce" <spearce@spearce.org> wrote:
> > Junio C Hamano <gitster@pobox.com> wrote:
> > > 
> > > Especially, I am not sure if the issue only exists at the
> > > end_packfile() boundary.  Don't we have the same issue reading
> > > from the packfile being built, and isn't the only reason my hack
> > > works it around is because access patterns of the testsuite
> > > happens to not trigger it?
> > 
> > Yes, that's my take on it as well (see my other email).  The
> > testsuite must just be really lucky that its not hitting the
> > boundary condition.
> > 
> > I almost said gfi_unpack_entry() was immune from this bug, but
> > I went back and read the code again and determined that it does
> > in fact suffer from this under NO_MMAP, and we're just really
> > damn lucky nobody has caused it.
> 
> I think this solves the problem.  Its based on your first patch, but
> would replace it.  The trick here is we close the cached windows if
> we are accessing data from the packfile we are appending into and we
> have increased the file length.  This way we don't blow away windows
> during high read/low write periods, like during branch cache reloads.

Junio pointed out my first attempt at this didn't update the
memory pressure values, so we could "run out of memory" even
if we had plenty free.

Try #2...
 
--8>--
diff --git a/cache.h b/cache.h
index 24735bd..15388aa 100644
--- a/cache.h
+++ b/cache.h
@@ -561,6 +561,7 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 extern void pack_report(void);
 extern int open_pack_index(struct packed_git *);
 extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
+extern void close_pack_windows(struct packed_git *, int);
 extern void unuse_pack(struct pack_window **);
 extern struct packed_git *add_packed_git(const char *, int, int);
 extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t);
diff --git a/fast-import.c b/fast-import.c
index 3609c24..82c82ce 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -917,6 +917,7 @@ static void end_packfile(void)
 		struct branch *b;
 		struct tag *t;
 
+		close_pack_windows(pack_data, 0);
 		fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
 				    pack_data->pack_name, object_count);
 		close(pack_data->pack_fd);
@@ -926,7 +927,6 @@ static void end_packfile(void)
 		new_p = add_packed_git(idx_name, strlen(idx_name), 1);
 		if (!new_p)
 			die("core git rejected index %s", idx_name);
-		new_p->windows = old_p->windows;
 		all_packs[pack_id] = new_p;
 		install_packed_git(new_p);
 
@@ -1129,8 +1129,10 @@ static void *gfi_unpack_entry(
 {
 	enum object_type type;
 	struct packed_git *p = all_packs[oe->pack_id];
-	if (p == pack_data)
+	if (p == pack_data && p->pack_size < (pack_size + 20)) {
+		close_pack_windows(p, 0);
 		p->pack_size = pack_size + 20;
+	}
 	return unpack_entry(p, oe->offset, &type, sizep);
 }
 
diff --git a/sha1_file.c b/sha1_file.c
index 6583797..50d1dea 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -611,6 +611,34 @@ void release_pack_memory(size_t need, int fd)
 		; /* nothing */
 }
 
+void close_pack_windows(struct packed_git *p, int retain_fd)
+{
+	struct pack_window **tail = NULL, *n = p->windows;
+	while (n) {
+		struct pack_window *w = p->windows;
+
+		if (w->inuse_cnt) {
+			*tail = w;
+			tail = &w->next;
+			continue;
+		}
+
+		munmap(w->base, w->len);
+		pack_mapped -= w->len;
+		pack_open_windows--;
+		n = w->next;
+		free(w);
+	}
+
+	p->windows = *tail;
+	if (p->windows)
+		warning("pack windows still in-use during close attempt");
+	else if (!retain_fd && p->pack_fd != -1) {
+		close(p->pack_fd);
+		p->pack_fd = -1;
+	}
+}
+
 void unuse_pack(struct pack_window **w_cursor)
 {
 	struct pack_window *w = *w_cursor;

-- 
Shawn.

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

* Re: Be more careful about updating refs
  2008-01-18  3:18                         ` Shawn O. Pearce
@ 2008-01-18  3:22                           ` Shawn O. Pearce
       [not found]                             ` <20080118035700.GA3458@spearce.org>
  0 siblings, 1 reply; 36+ messages in thread
From: Shawn O. Pearce @ 2008-01-18  3:22 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Sixt, Charles Bailey, Linus Torvalds, Git Mailing List

"Shawn O. Pearce" <spearce@spearce.org> wrote:
> "Shawn O. Pearce" <spearce@spearce.org> wrote:
> > "Shawn O. Pearce" <spearce@spearce.org> wrote:
> > > Junio C Hamano <gitster@pobox.com> wrote:
> > > > 
> > > > Especially, I am not sure if the issue only exists at the
> > > > end_packfile() boundary.  Don't we have the same issue reading
> > > > from the packfile being built, and isn't the only reason my hack
> > > > works it around is because access patterns of the testsuite
> > > > happens to not trigger it?
> > > 
> > > Yes, that's my take on it as well (see my other email).  The
> > > testsuite must just be really lucky that its not hitting the
> > > boundary condition.
> > > 
> > > I almost said gfi_unpack_entry() was immune from this bug, but
> > > I went back and read the code again and determined that it does
> > > in fact suffer from this under NO_MMAP, and we're just really
> > > damn lucky nobody has caused it.
> > 
> > I think this solves the problem.  Its based on your first patch, but
> > would replace it.  The trick here is we close the cached windows if
> > we are accessing data from the packfile we are appending into and we
> > have increased the file length.  This way we don't blow away windows
> > during high read/low write periods, like during branch cache reloads.
> 
> Junio pointed out my first attempt at this didn't update the
> memory pressure values, so we could "run out of memory" even
> if we had plenty free.
> 
> Try #2...

OK, that was crap.  Don't even try it.

I'm holding off sending anything more until I get the test suite
to actually run without telling me the bus crashed into the wall.

-- 
Shawn.

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
       [not found]                             ` <20080118035700.GA3458@spearce.org>
@ 2008-01-18  4:27                               ` Linus Torvalds
  2008-01-18  8:42                                 ` Charles Bailey
  2008-01-18  6:10                               ` Junio C Hamano
                                                 ` (2 subsequent siblings)
  3 siblings, 1 reply; 36+ messages in thread
From: Linus Torvalds @ 2008-01-18  4:27 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: Junio C Hamano, git, Johannes Sixt, Charles Bailey



On Thu, 17 Jan 2008, Shawn O. Pearce wrote:
>
> fast-import was relying on the fact that on most systems mmap() and
> write() are synchronized by the filesystem's buffer cache.  We were
> relying on the ability to mmap() 20 bytes beyond the current end
> of the file, then later fill in those bytes with a future write()
> call, then read them through the previously obtained mmap() address.
> 
> This isn't always true with some implementations of NFS, but it is
> especially not true with our NO_MMAP=YesPlease build time option used
> on some platforms.

In fact, even with mmap(), it's not guaranteed. There are really crappy 
mmap implementations out there, partly due to bad CPU design (virtual CPU 
caches without coherency), but more often due to total crap OS.

(Yeah, Linux did count in that area at some point. Long ago. Early 90's. 
Maybe)

I think HP-UX used to have non-coherent mmap for the longest time, due to 
carrying around some totally crap memory management based on some ancient 
BSD version that everybody else (including the BSD's) had long since 
jettisoned.

That said, I suspect any unix you can run today (without calling it a 
retro setup) probably has coherent-enough mmap. The possible virtual cache 
coherency issue is unlikely to be able to trigger this (and not relevant 
on any sane hardware anyway).

				Linus

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
       [not found]                             ` <20080118035700.GA3458@spearce.org>
  2008-01-18  4:27                               ` [PATCH] Fix random fast-import errors when compiled with NO_MMAP Linus Torvalds
@ 2008-01-18  6:10                               ` Junio C Hamano
  2008-01-21  4:10                                 ` Shawn O. Pearce
  2008-01-18  7:53                               ` Johannes Sixt
  2008-01-18  9:26                               ` Charles Bailey
  3 siblings, 1 reply; 36+ messages in thread
From: Junio C Hamano @ 2008-01-18  6:10 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git, Johannes Sixt, Charles Bailey, Linus Torvalds

"Shawn O. Pearce" <spearce@spearce.org> writes:

> +extern void close_pack_windows(struct packed_git *, int);

Nobody seems to pass anything but true in retain_fd parameter.
Is it worth it?

> +void close_pack_windows(struct packed_git *p, int retain_fd)
> +{
> +	struct pack_window *tail_var = NULL, *n = p->windows;
> +	struct pack_window **tail = &tail_var;
> +	while (n) {
> +		struct pack_window *w = p->windows;
> +
> +		if (w->inuse_cnt) {
> +			*tail = w;
> +			tail = &w->next;
> +			continue;
> +		}
> +
> +		munmap(w->base, w->len);
> +		pack_mapped -= w->len;
> +		pack_open_windows--;
> +		n = w->next;
> +		free(w);
> +	}
> +
> +	p->windows = tail_var;
> +	if (p->windows)
> +		warning("pack windows still in-use during close attempt");
> +	else if (!retain_fd && p->pack_fd != -1) {
> +		close(p->pack_fd);
> +		p->pack_fd = -1;
> +	}
> +}

I am not sure about this inuse_cnt business.

The only caller we know that needs this function is fast-import
that wants to drop all windows into a pack while keeping the
file descriptor and it should not have an in-use windows.

It is unclear what semantics is the right one for callers that
do want to retain some windows but still want to call this
function.  If somebody is in the middle of chasing a delta chain
and still calls this function, *why* is the call being made, and
what is the right behaviour if not all the windows can be closed
because of these open windows?

What about the case the value passed in ratain_fd is 0, which
presumably means the caller is asking us to close the file
descriptor?  If we close the packfile, later accesses through
the unclosed windows will obviously barf and I understand that
is why you are ignoring retain_fd in that case, but maybe for
the caller it was of higher priority that the packfile to get
closed than the remaining windows to be still usable.  Or maybe
the caller wants to be notified of such an error, in which case
it probably is not enough to just call warning().

IOW, I think the patch is trying to be too flexible without
having a clear definition of what that flexibility is trying to
achieve.

Maybe we would need more flexible version later, but I am not
convinced if the semantics the above patch implements is the
right one.

So I'd prefer something much simpler like this one instead...

void close_pack_windows(struct packed_git *p)
{
	while (p->windows) {
		struct pack_window *w = p->windows;

		if (w->inuse_cnt)
			die("pack '%s' still has outstanding windows",
				p->pack_name)
		munmap(w->base, w->len);
		pack_mapped -= w->len;
		pack_open_windows--;
		p->windows = w->next;
		free(w);
	}
}

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
       [not found]                             ` <20080118035700.GA3458@spearce.org>
  2008-01-18  4:27                               ` [PATCH] Fix random fast-import errors when compiled with NO_MMAP Linus Torvalds
  2008-01-18  6:10                               ` Junio C Hamano
@ 2008-01-18  7:53                               ` Johannes Sixt
  2008-01-18  9:26                               ` Charles Bailey
  3 siblings, 0 replies; 36+ messages in thread
From: Johannes Sixt @ 2008-01-18  7:53 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: Junio C Hamano, git, Charles Bailey, Linus Torvalds

Shawn O. Pearce schrieb:
>  This should do the trick.  I SOB'd the patch as I did test it with
>  NO_MMAP and I think its valid.  :-)

Thanks a lot. It works here on Windows, too.

-- Hannes

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
  2008-01-18  4:27                               ` [PATCH] Fix random fast-import errors when compiled with NO_MMAP Linus Torvalds
@ 2008-01-18  8:42                                 ` Charles Bailey
  2008-01-18 17:08                                   ` Linus Torvalds
  0 siblings, 1 reply; 36+ messages in thread
From: Charles Bailey @ 2008-01-18  8:42 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Shawn O. Pearce, Junio C Hamano, git, Johannes Sixt

On Thu, Jan 17, 2008 at 08:27:08PM -0800, Linus Torvalds wrote:
> 
> 
> On Thu, 17 Jan 2008, Shawn O. Pearce wrote:
> >
> > fast-import was relying on the fact that on most systems mmap() and
> > write() are synchronized by the filesystem's buffer cache.  We were
> > relying on the ability to mmap() 20 bytes beyond the current end
> > of the file, then later fill in those bytes with a future write()
> > call, then read them through the previously obtained mmap() address.
> > 
> > This isn't always true with some implementations of NFS, but it is
> > especially not true with our NO_MMAP=YesPlease build time option used
> > on some platforms.
> 
> In fact, even with mmap(), it's not guaranteed. There are really crappy 
> mmap implementations out there, partly due to bad CPU design (virtual CPU 
> caches without coherency), but more often due to total crap OS.
> 
> (Yeah, Linux did count in that area at some point. Long ago. Early 90's. 
> Maybe)
> 
> I think HP-UX used to have non-coherent mmap for the longest time, due to 
> carrying around some totally crap memory management based on some ancient 
> BSD version that everybody else (including the BSD's) had long since 
> jettisoned.
> 
> That said, I suspect any unix you can run today (without calling it a 
> retro setup) probably has coherent-enough mmap. The possible virtual cache 
> coherency issue is unlikely to be able to trigger this (and not relevant 
> on any sane hardware anyway).
> 
> 				Linus

I've just checked the Mac OS X build and it looks like there is a mmap
and git is indeed using it, so this is obviously an example of a
"really crappy" mmap implementation.

This adds more ammunition to the fight against the whole Mac OS X is
powered/built/based on UNIX myth.

Charles.

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
       [not found]                             ` <20080118035700.GA3458@spearce.org>
                                                 ` (2 preceding siblings ...)
  2008-01-18  7:53                               ` Johannes Sixt
@ 2008-01-18  9:26                               ` Charles Bailey
  2008-01-18  9:36                                 ` Junio C Hamano
  3 siblings, 1 reply; 36+ messages in thread
From: Charles Bailey @ 2008-01-18  9:26 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: Junio C Hamano, git, Johannes Sixt, Linus Torvalds

I can ACK this as fixing the test suite failures on Mac OS X 10.4.11
both with and without NO_MMAP=Yes on top of 1.5.4.rc3.24.gb53139 and
also on my oldish Fedora install with NO_MMAP=Yes.

Acked-by: Charles Bailey <charles@hashpling.org>

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
  2008-01-18  9:26                               ` Charles Bailey
@ 2008-01-18  9:36                                 ` Junio C Hamano
  2008-01-18  9:45                                   ` Charles Bailey
  0 siblings, 1 reply; 36+ messages in thread
From: Junio C Hamano @ 2008-01-18  9:36 UTC (permalink / raw)
  To: Charles Bailey; +Cc: Shawn O. Pearce, git, Johannes Sixt, Linus Torvalds

Charles Bailey <charles@hashpling.org> writes:

> I can ACK this as fixing the test suite failures on Mac OS X 10.4.11
> both with and without NO_MMAP=Yes on top of 1.5.4.rc3.24.gb53139 and
> also on my oldish Fedora install with NO_MMAP=Yes.
>
> Acked-by: Charles Bailey <charles@hashpling.org>

Well, I do not want to be picky with these things, but you do
not own that area, so that would be "Tested-by:"

I've already committed the simplified version I suggested to
Shawn.

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
  2008-01-18  9:36                                 ` Junio C Hamano
@ 2008-01-18  9:45                                   ` Charles Bailey
  2008-01-18 10:57                                     ` Junio C Hamano
  0 siblings, 1 reply; 36+ messages in thread
From: Charles Bailey @ 2008-01-18  9:45 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Shawn O. Pearce, git, Johannes Sixt, Linus Torvalds

On Fri, Jan 18, 2008 at 01:36:55AM -0800, Junio C Hamano wrote:
> 
> Well, I do not want to be picky with these things, but you do
> not own that area, so that would be "Tested-by:"
> 
> I've already committed the simplified version I suggested to
> Shawn.
> 

Sorry, I thought that Acked-by was an acceptable tag for people not
involved in the development path of a patch.  I don't want to add a
"Tested-by" as I am not a fast-import user and I am *only* reporting
the success of the test suite, not any real-world testing.

Charles.

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
  2008-01-18  9:45                                   ` Charles Bailey
@ 2008-01-18 10:57                                     ` Junio C Hamano
  0 siblings, 0 replies; 36+ messages in thread
From: Junio C Hamano @ 2008-01-18 10:57 UTC (permalink / raw)
  To: Charles Bailey; +Cc: git

Charles Bailey <charles@hashpling.org> writes:

> Sorry, I thought that Acked-by was an acceptable tag for people not
> involved in the development path of a patch.  I don't want to add a
> "Tested-by" as I am not a fast-import user and I am *only* reporting
> the success of the test suite, not any real-world testing.

It still counts as "Tested-by:".  Earlier you saw breakage and
now you see it fixed in your environment, to which neither I nor
Shawn have access to.  That's a good verification that the patch
fixed the issue for you and your feedback was very much
appreciated (your timely initial breakage report even more so).

Acked-by: is usually given by people who are the most familiar
with the code being affected (or at least by the ones more
familiar than the patch's author), in order to let me and the
world know that he thinks the contents of the patch makes sense.
IOW, the patch was checked by somebody who knows the existing
code well enough to catch any subtleties in the existing code
that might have been broken if the patch's author was not
careful.  It just felt slightly funny that anybody is giving an
Acked-by: to a patch Shawn made to fast-import.c, which is his
brainchild ;-).

Signed-off-by is about the kosherness from software license
point of view, while Acked-by and Tested-by are about the
correctness.

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
  2008-01-18  8:42                                 ` Charles Bailey
@ 2008-01-18 17:08                                   ` Linus Torvalds
  2008-01-19  3:25                                     ` Junio C Hamano
  0 siblings, 1 reply; 36+ messages in thread
From: Linus Torvalds @ 2008-01-18 17:08 UTC (permalink / raw)
  To: Charles Bailey; +Cc: Shawn O. Pearce, Junio C Hamano, git, Johannes Sixt



On Fri, 18 Jan 2008, Charles Bailey wrote:
> 
> I've just checked the Mac OS X build and it looks like there is a mmap
> and git is indeed using it, so this is obviously an example of a
> "really crappy" mmap implementation.

Looking closer, this is not necessarily the case here.

Git uses MAP_PRIVATE, because that whole pack-file mapping was really 
*meant* to map an existing read-only pack-file, and fast-import seems to 
really be screwing with it.

It so happens that Linux has a particularly clean and streamlined VM, and 
if you do only reads to a MAP_PRIVATE mapping on a normal filesystem, 
you'll always be as coherent as with MAP_SHARED because Linux will simply 
map in the page cache pages directly.

But this is definitely not portable, and the git fast-import mmap window 
usage before Shawn's patch it was simply wrong.

So in this case, it really was git that was crap.

It just happened to work because the Linux mmap handling is just generally 
pretty sane. It probably also worked fine on pretty much any other modern 
UNIX (ie Solaris).

I'm not quite sure what OS X does to MAP_PRIVATE mappings, but if OS X is 
still based on Mach (with FreeBSD just as a single-server on top), I 
suspect that may be why it broke on OS X. The Mach VM is insanely complex 
and does really odd things.

But the fact is, without MAP_SHARED, you shouldn't expect things to be 
coherent, even if they often will be (especially for PROT_READ).

Btw, even with Shawn's patch, I wonder if the index_data usage is correct.

			Linus

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
  2008-01-18 17:08                                   ` Linus Torvalds
@ 2008-01-19  3:25                                     ` Junio C Hamano
  2008-01-19  3:55                                       ` Linus Torvalds
  2008-01-21  3:57                                       ` Shawn O. Pearce
  0 siblings, 2 replies; 36+ messages in thread
From: Junio C Hamano @ 2008-01-19  3:25 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Charles Bailey, Shawn O. Pearce, git, Johannes Sixt

Linus Torvalds <torvalds@linux-foundation.org> writes:

> Btw, even with Shawn's patch, I wonder if the index_data usage is correct.

Hmph.

gfi uses data in a "pack" in two quite different ways.

A new object is written to an unfinalized pack.  Such a pack
already has "struct packed_git" allocated for it and a pointer
to it is held in pack_data.  As far as the core part of git
(that is, sha1_file.c) is concerned, however, this pack does not
even exist.  It is still not part of packed_git list in
sha1_file.c, and read_sha1_file() will not see objects in it, as
no idx into the packfile exists yet.  gfi has a table of objects
in this pack and uses gfi_unpack_entry() function to retrieve
data from it.

A packfile is finalized in end_packfile().  The pack header and
footer is recomputed, an idx file is written, and the pack is
finally registered.  Before that time p->index_data is not even
used for that pack (it is initialized with NULL).

So I do not think "index_data usage" can be incorrect, as there
won't be any index_data usage with unfinalized pack, and the
core part of git would not even have any mmap(2) (nor open fd)
into its idx file before it is finalized.

By the way, I was quite puzzled how the gfi_unpack_entry()
function manages to work correctly when it has to read an object
it deltified based on another object it wrote into the same
unfinalized pack earlier.  It knows where in the unfinalized
pack it wrote the object, so it can find from its own "struct
object_entry" the offset for the object, and calls
unpack_entry() defined in the core to do the rest.

However, most of the core does not really know about the other
objects in this half-built pack.  If the object is a delta,
unpack_delta_entry() needs to find the delta base.  And it needs
to do that without having the idx.

The trick (the code really needs a bit more documentation) is
that gfi never writes anything but OFS_DELTA.  So the core, even
though it does not have the corresponding idx file, does not
have to look up the object (in fact it does not even know what
object to look up for the base, it only knows the offset).

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
  2008-01-19  3:25                                     ` Junio C Hamano
@ 2008-01-19  3:55                                       ` Linus Torvalds
  2008-01-21  3:57                                       ` Shawn O. Pearce
  1 sibling, 0 replies; 36+ messages in thread
From: Linus Torvalds @ 2008-01-19  3:55 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Charles Bailey, Shawn O. Pearce, git, Johannes Sixt



On Fri, 18 Jan 2008, Junio C Hamano wrote:
> 
> gfi uses data in a "pack" in two quite different ways.
> 
> A new object is written to an unfinalized pack.  Such a pack
> already has "struct packed_git" allocated for it and a pointer
> to it is held in pack_data.  As far as the core part of git
> (that is, sha1_file.c) is concerned, however, this pack does not
> even exist.  It is still not part of packed_git list in
> sha1_file.c, and read_sha1_file() will not see objects in it, as
> no idx into the packfile exists yet.  gfi has a table of objects
> in this pack and uses gfi_unpack_entry() function to retrieve
> data from it.
> 
> A packfile is finalized in end_packfile().  The pack header and
> footer is recomputed, an idx file is written, and the pack is
> finally registered.  Before that time p->index_data is not even
> used for that pack (it is initialized with NULL).

Oh, ok. I did try to grep for index_data, and didn't find anything that 
looked bad, but the incestuous things that fast-import.c does just made me 
worry - but I was too lazy to really follow it all. It's one of those 
files I don't think I've ever had anything what-so-ever to do with.

> So I do not think "index_data usage" can be incorrect, as there
> won't be any index_data usage with unfinalized pack, and the
> core part of git would not even have any mmap(2) (nor open fd)
> into its idx file before it is finalized.

In that case, I think Shawn fixed it all, and we're all good, and it's not 
just hidden well enough that it "just happens" to work.

				Linus

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
  2008-01-19  3:25                                     ` Junio C Hamano
  2008-01-19  3:55                                       ` Linus Torvalds
@ 2008-01-21  3:57                                       ` Shawn O. Pearce
  1 sibling, 0 replies; 36+ messages in thread
From: Shawn O. Pearce @ 2008-01-21  3:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, Charles Bailey, git, Johannes Sixt

Junio C Hamano <gitster@pobox.com> wrote:
> Linus Torvalds <torvalds@linux-foundation.org> writes:
> 
> > Btw, even with Shawn's patch, I wonder if the index_data usage is correct.
...
> So I do not think "index_data usage" can be incorrect, as there
> won't be any index_data usage with unfinalized pack, and the
> core part of git would not even have any mmap(2) (nor open fd)
> into its idx file before it is finalized.

Correct.
 
> By the way, I was quite puzzled how the gfi_unpack_entry()
> function manages to work correctly when it has to read an object
> it deltified based on another object it wrote into the same
> unfinalized pack earlier.  It knows where in the unfinalized
> pack it wrote the object, so it can find from its own "struct
> object_entry" the offset for the object, and calls
> unpack_entry() defined in the core to do the rest.
> 
> However, most of the core does not really know about the other
> objects in this half-built pack.  If the object is a delta,
> unpack_delta_entry() needs to find the delta base.  And it needs
> to do that without having the idx.
> 
> The trick (the code really needs a bit more documentation) is
> that gfi never writes anything but OFS_DELTA.  So the core, even
> though it does not have the corresponding idx file, does not
> have to look up the object (in fact it does not even know what
> object to look up for the base, it only knows the offset).

Yup.

Older fast-imports (pre OFS_DELTA) had to replicate a good chunk of
the unpack_entry() logic directly inside of fast-import.  But it
later occurred to me that OFS_DELTA simplifies the code and lets
me reuse more of the existing sha1_file.c implementation.

So yea, fast-import only emits OFS_DELTA, and does so only so that it
can pull this delta-base-unpack trick on the core, without actually
giving the core the corresponding idx file first.

-- 
Shawn.

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

* Re: [PATCH] Fix random fast-import errors when compiled with NO_MMAP
  2008-01-18  6:10                               ` Junio C Hamano
@ 2008-01-21  4:10                                 ` Shawn O. Pearce
  0 siblings, 0 replies; 36+ messages in thread
From: Shawn O. Pearce @ 2008-01-21  4:10 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Sixt, Charles Bailey, Linus Torvalds

Junio C Hamano <gitster@pobox.com> wrote:
> "Shawn O. Pearce" <spearce@spearce.org> writes:
> 
> > +extern void close_pack_windows(struct packed_git *, int);
> 
> Nobody seems to pass anything but true in retain_fd parameter.
> Is it worth it?

Hmm, well, I originally wrote something like what you fixed it
to be, then thought that at some point in the future someone may
try to invoke the method and expect different semantics than it
implemented.  :-)

In short I made the implementation I gave you way too complex.
 
> So I'd prefer something much simpler like this one instead...
> 
> void close_pack_windows(struct packed_git *p)
> {
> 	while (p->windows) {
> 		struct pack_window *w = p->windows;
> 
> 		if (w->inuse_cnt)
> 			die("pack '%s' still has outstanding windows",
> 				p->pack_name)
> 		munmap(w->base, w->len);
> 		pack_mapped -= w->len;
> 		pack_open_windows--;
> 		p->windows = w->next;
> 		free(w);
> 	}
> }

Me too.  Thank you for the sanity check, and for fixing the
code to actually be somewhat sane.

-- 
Shawn.

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

* Re: Be more careful about updating refs
  2008-01-15 23:50 Be more careful about updating refs Linus Torvalds
                   ` (2 preceding siblings ...)
  2008-01-16  1:11 ` Junio C Hamano
@ 2008-01-23 22:53 ` Sam Vilain
  3 siblings, 0 replies; 36+ messages in thread
From: Sam Vilain @ 2008-01-23 22:53 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List, Junio C Hamano

Linus Torvalds wrote:
> Perhaps more importantly, it also refuses to update a branch head with a 
> non-commit object. I don't quite know *how* the stable series maintainers 
> were able to corrupt their repository to have a HEAD that pointed to a tag 
> rather than a commit object, but they did. Which results in a totally 
> broken repository that cannot be cloned or committed on.

I actually used this for a prototype system to allow push over git://
with secure authentication via PGP; basically it used an update hook
that disallowed any ref change that wasn't a signed tag, and then in the
post-update hook, moved the tag to an audit log refspace and put the
referant commit in the refs/heads/* location.

The hooks (probably racy etc) can be seen under
http://git.utsl.gen.nz/Tangram/hooks

Sam.

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

end of thread, other threads:[~2008-01-23 22:54 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-01-15 23:50 Be more careful about updating refs Linus Torvalds
2008-01-16  0:02 ` Linus Torvalds
2008-01-16 19:52   ` Junio C Hamano
2008-01-17  9:15     ` Charles Bailey
2008-01-17 10:52       ` Johannes Sixt
2008-01-17 11:01         ` Charles Bailey
2008-01-17 12:41           ` Johannes Sixt
2008-01-17 12:58             ` Johannes Schindelin
2008-01-17 13:07               ` Charles Bailey
2008-01-18  1:43             ` Junio C Hamano
2008-01-18  2:01               ` Junio C Hamano
2008-01-18  2:13                 ` Shawn O. Pearce
2008-01-18  2:25                   ` Junio C Hamano
2008-01-18  2:33                     ` Shawn O. Pearce
2008-01-18  2:58                       ` Shawn O. Pearce
2008-01-18  3:18                         ` Shawn O. Pearce
2008-01-18  3:22                           ` Shawn O. Pearce
     [not found]                             ` <20080118035700.GA3458@spearce.org>
2008-01-18  4:27                               ` [PATCH] Fix random fast-import errors when compiled with NO_MMAP Linus Torvalds
2008-01-18  8:42                                 ` Charles Bailey
2008-01-18 17:08                                   ` Linus Torvalds
2008-01-19  3:25                                     ` Junio C Hamano
2008-01-19  3:55                                       ` Linus Torvalds
2008-01-21  3:57                                       ` Shawn O. Pearce
2008-01-18  6:10                               ` Junio C Hamano
2008-01-21  4:10                                 ` Shawn O. Pearce
2008-01-18  7:53                               ` Johannes Sixt
2008-01-18  9:26                               ` Charles Bailey
2008-01-18  9:36                                 ` Junio C Hamano
2008-01-18  9:45                                   ` Charles Bailey
2008-01-18 10:57                                     ` Junio C Hamano
2008-01-18  2:30               ` Be more careful about updating refs Shawn O. Pearce
2008-01-17 10:56       ` Charles Bailey
2008-01-16  0:29 ` Junio C Hamano
2008-01-16  0:42   ` Linus Torvalds
2008-01-16  1:11 ` Junio C Hamano
2008-01-23 22:53 ` Sam Vilain

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).