git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH] object-file: use real paths when adding alternates

From
Glen Choo via GitGitGadget <gitgitgadget@gmail.com>
Date
Nov 17, 2022, 17:31 UTC
Message-ID
<pull.1382.git.git.1668706274099.gitgitgadget@gmail.com>
From: Glen Choo <chooglen@google.com>

When adding an alternate ODB, we check if the alternate has the same path as the object dir, and if so, we do nothing. However, that comparison does not resolve symlinks. This makes it possible to add the object dir as an alternate, which may result in bad behavior. For example, it can trick "git repack -a -l -d" (possibly run by "git gc") into thinking that all packs come from an alternate and delete all objects.

	rm -rf test &&
	git clone https://github.com/git/git test &&
	(
	cd test &&
	ln -s objects .git/alt-objects &&
	# -c repack.updateserverinfo=false silences a warning about not
	# being able to update "info/refs", it isn't needed to show the
	# bad behavior
	GIT_ALTERNATE_OBJECT_DIRECTORIES=".git/alt-objects" git \
		-c repack.updateserverinfo=false repack -a -l -d  &&
	# It's broken!
	git status
	# Because there are no more objects!
	ls .git/objects/pack
	)

Fix this by resolving symlinks before comparing the alternate and object dir.

Signed-off-by: Glen Choo <chooglen@google.com>
---
    object-file: use real paths when adding alternates
    
    Here's a bug that got uncovered because of some oddities in how "repo"
    [1] manages its object directories. With some tracing, I'm quite certain
    that the mechanism is that the packs are treated as non-local, but I
    don't understand "git repack" extremely well, so e.g. the test I added
    seems pretty crude.
    
    [1] https://gerrit.googlesource.com/git-repo
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1382%2Fchooglen%2Fobject-file%2Fcheck-alternate-real-path-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1382/chooglen/object-file/check-alternate-real-path-v1
Pull-Request: https://github.com/git/git/pull/1382
 object-file.c     | 17 ++++++++++++-----
 t/t7700-repack.sh | 18 ++++++++++++++++++
 2 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/object-file.c b/object-file.c
index 957790098fa..f901dd272d1 100644
--- a/object-file.c
+++ b/object-file.c
@@ -455,14 +455,16 @@ static int alt_odb_usable(struct raw_object_store *o,
 			  struct strbuf *path,
 			  const char *normalized_objdir, khiter_t *pos)
 {
+	int ret = 0;
 	int r;
+	struct strbuf real_path = STRBUF_INIT;
 
 	/* Detect cases where alternate disappeared */
 	if (!is_directory(path->buf)) {
 		error(_("object directory %s does not exist; "
 			"check .git/objects/info/alternates"),
 		      path->buf);
-		return 0;
+		goto cleanup;
 	}
 
 	/*
@@ -478,11 +480,16 @@ static int alt_odb_usable(struct raw_object_store *o,
 		assert(r == 1); /* never used */
 		kh_value(o->odb_by_path, p) = o->odb;
 	}
-	if (fspatheq(path->buf, normalized_objdir))
-		return 0;
+
+	strbuf_realpath(&real_path, path->buf, 1);
+	if (fspatheq(real_path.buf, normalized_objdir))
+		goto cleanup;
 	*pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r);
 	/* r: 0 = exists, 1 = never used, 2 = deleted */
-	return r == 0 ? 0 : 1;
+	ret = r == 0 ? 0 : 1;
+ cleanup:
+	strbuf_release(&real_path);
+	return ret;
 }
 
 /*
@@ -596,7 +603,7 @@ static void link_alt_odb_entries(struct repository *r, const char *alt,
 		return;
 	}
 
-	strbuf_add_absolute_path(&objdirbuf, r->objects->odb->path);
+	strbuf_realpath(&objdirbuf, r->objects->odb->path, 1);
 	if (strbuf_normalize_path(&objdirbuf) < 0)
 		die(_("unable to normalize object directory: %s"),
 		    objdirbuf.buf);
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
index 5be483bf887..ce1954d0977 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -90,6 +90,24 @@ test_expect_success 'loose objects in alternate ODB are not repacked' '
 	test_has_duplicate_object false
 '
 
+test_expect_success '--local keeps packs when alternate is objectdir ' '
+	git init alt_symlink &&
+	(
+		cd alt_symlink &&
+		git init &&
+		echo content >file4 &&
+		git add file4 &&
+		git commit -m commit_file4 &&
+		git repack -a &&
+		ls .git/objects/pack/*.pack >../expect &&
+		ln -s objects .git/alt_objects &&
+		echo "$(pwd)/.git/alt_objects" >.git/objects/info/alternates &&
+		git repack -a -d -l &&
+		ls .git/objects/pack/*.pack >../actual
+	) &&
+	test_cmp expect actual
+'
+
 test_expect_success 'packed obs in alt ODB are repacked even when local repo is packless' '
 	mkdir alt_objects/pack &&
 	mv .git/objects/pack/* alt_objects/pack &&

base-commit: 319605f8f00e402f3ea758a02c63534ff800a711
-- 
gitgitgadget
Next: Jeff King
Message 1 of 17 in “object-file: use real paths when adding alternates”
  1. object-file: use real paths when adding alternatesGlen Choo via GitGitGadget, Nov 17, 2022
  2. Jeff KingNov 17, 2022
  3. Ævar Arnfjörð BjarmasonNov 17, 2022
  4. Jeff KingNov 17, 2022
  5. Taylor BlauNov 17, 2022
  6. Glen ChooNov 18, 2022
  7. Taylor BlauNov 17, 2022
  8. object-file: use real paths when adding alternatesGlen Choo via GitGitGadget, Nov 21, 2022
  9. Ævar Arnfjörð BjarmasonNov 22, 2022
  10. Jeff KingNov 22, 2022
  11. Glen ChooNov 24, 2022
  12. Jeff KingNov 24, 2022
  13. Glen ChooNov 24, 2022
  14. Jeff KingNov 22, 2022
  15. object-file: use real paths when adding alternatesGlen Choo via GitGitGadget, Nov 24, 2022
  16. Jeff KingNov 24, 2022
  17. Junio C HamanoNov 25, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.