git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: "Sun Chao via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Sun Chao <16657101987@163.com>, Sun Chao <sunchao9@huawei.com>
Subject: [PATCH 1/3] refs-advertise: add hook to filter advertised refs
Date: Wed, 03 Aug 2022 16:17:35 +0000	[thread overview]
Message-ID: <b4b5ce5a361caf08ea15a8066f6cac5b255a527d.1659543457.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1301.git.git.1659543457.gitgitgadget@gmail.com>

From: Sun Chao <sunchao9@huawei.com>

Gerrit is implemented by JGit and is known as a centralized workflow system
which supports reference-level access control for repository. If we choose
to work in centralized workflow like what Gerrit provided, reference-level
access control is needed and is possible if we add a reference advertise
filter hook just like what Gerrit did.

This hook would be invoked by 'git-receive-pack' and 'git-upload-pack'
during the reference discovery phase and the commit fetching phase,
each reference and will be filtered by this hook. The hook executes once
with no arguments for each 'git-upload-pack' and 'git-receive-pack' process
and if the exit status is non-zero, `git push` and `git fetch` will abort.

Once the hook is invoked, a version number and server process name
('git-upload-pack' or 'git-receive-pack' or 'ls-refs') should send to it in
packet-line format, followed by a flush-pkt. The hook should response with
its version number and process name list it support. If the list does not
contains the server process name, the server will close the connection with
the hook and keep going without the hook child process.

During reference discovery phase, each reference will be filtered by this
hook. In the following example, the letter 'G' stands for 'git-receive-pack'
or 'git-upload-pack' and the letter 'H' stands for this hook. The hook
decides if the reference will be advertised or not, it sends result back in
pkt-line format protocol, a response in "ok ref <ref>" format followed by a
flush-pkt means the references "<ref>" can be advertised, and "ng ref <ref>"
means not.

    # Version negotiation
    G: PKT-LINE(version=1\0git-upload-pack)
    G: flush-pkt
    H: PKT-LINE(version=1\0git-upload-pack git-receive-pack ls-refs)
    H: flush-pkt

    # Send reference filter request to hook
    G: PKT-LINE(ref <ref> <oid>)
    G: flush-pkt

    # Receive result from the hook.
    # Case 1: this reference is valid
    H: PKT-LINE(ok ref <ref>)
    H: flush-pkt
    # Case 2: this reference is filtered out
    H: PKT-LINE(ng ref <ref>)
    H: flush-pkt

During commit fetch phase of 'git-upload-pack' process, git client may send
`want <oid>` requests and 'git-upload-pack' will send object filter requests
to the hook to check if the object "<oid>" will be sent to the client or
not. In the following example, the letter 'G' stands for 'git-upload-pack'
and the letter 'H' stands for this hook.

The hook will decides if a commit will be sent to the client or not, it
sends result in pkt-line format protocol to `git-upload-pack`, a response
with "ok obj <oid>" format followed by a flush-pkt means the object "<oid>"
can be sent to client, and "ng obj <oid>" means not.

    # Version negotiation
    G: PKT-LINE(version=1\0ls-refs)
    G: flush-pkt
    H: PKT-LINE(version=1\0git-upload-pack git-receive-pack ls-refs)
    H: flush-pkt

    # Send commit filter request to hook
    G: PKT-LINE(obj <oid>)
    G: flush-pkt

    # Receive result from the hook.
    # Case 1: this object is valid
    H: PKT-LINE(ok obj <oid>)
    H: flush-pkt
    # Case 2: this object is filtered out
    H: PKT-LINE(ng obj <oid>)
    H: flush-pkt

Signed-off-by: Sun Chao <sunchao9@huawei.com>
---
 Makefile                       |   2 +
 builtin/receive-pack.c         |  12 ++
 ls-refs.c                      |  30 +++-
 refs/refs-advertise.c          | 246 +++++++++++++++++++++++++++++++++
 refs/refs-advertise.h          |  11 ++
 t/helper/test-refs-advertise.c | 202 +++++++++++++++++++++++++++
 t/helper/test-tool.c           |   1 +
 t/helper/test-tool.h           |   1 +
 upload-pack.c                  |  42 +++++-
 9 files changed, 538 insertions(+), 9 deletions(-)
 create mode 100644 refs/refs-advertise.c
 create mode 100644 refs/refs-advertise.h
 create mode 100644 t/helper/test-refs-advertise.c

diff --git a/Makefile b/Makefile
index 1624471badc..38740a52cee 100644
--- a/Makefile
+++ b/Makefile
@@ -793,6 +793,7 @@ TEST_BUILTINS_OBJS += test-wildmatch.o
 TEST_BUILTINS_OBJS += test-windows-named-pipe.o
 TEST_BUILTINS_OBJS += test-write-cache.o
 TEST_BUILTINS_OBJS += test-xml-encode.o
+TEST_BUILTINS_OBJS += test-refs-advertise.o
 
 # Do not add more tests here unless they have extra dependencies. Add
 # them in TEST_BUILTINS_OBJS above.
@@ -1050,6 +1051,7 @@ LIB_OBJS += refs/files-backend.o
 LIB_OBJS += refs/iterator.o
 LIB_OBJS += refs/packed-backend.o
 LIB_OBJS += refs/ref-cache.o
+LIB_OBJS += refs/refs-advertise.o
 LIB_OBJS += refspec.o
 LIB_OBJS += remote.o
 LIB_OBJS += replace-object.o
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 31b48e728be..2f82fd05f25 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -30,6 +30,7 @@
 #include "commit-reach.h"
 #include "worktree.h"
 #include "shallow.h"
+#include "refs/refs-advertise.h"
 
 static const char * const receive_pack_usage[] = {
 	N_("git receive-pack <git-dir>"),
@@ -299,6 +300,10 @@ static int show_ref_cb(const char *path_full, const struct object_id *oid,
 	if (ref_is_hidden(path, path_full))
 		return 0;
 
+	if (filter_advertise_ref(path, oid)) {
+		return 0;
+	}
+
 	/*
 	 * Advertise refs outside our current namespace as ".have"
 	 * refs, so that the client can use them to minimize data
@@ -2458,6 +2463,10 @@ static int delete_only(struct command *commands)
 	return 1;
 }
 
+static void clean_refs_advertise_filter(void) {
+	clean_advertise_refs_filter();
+}
+
 int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 {
 	int advertise_refs = 0;
@@ -2492,6 +2501,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 	if (!enter_repo(service_dir, 0))
 		die("'%s' does not appear to be a git repository", service_dir);
 
+	create_advertise_refs_filter("git-receive-pack");
+	atexit(clean_refs_advertise_filter);
+
 	git_config(receive_pack_config, NULL);
 	if (cert_nonce_seed)
 		push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL));
diff --git a/ls-refs.c b/ls-refs.c
index 98e69373c84..5a1f74abe0a 100644
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -6,10 +6,13 @@
 #include "ls-refs.h"
 #include "pkt-line.h"
 #include "config.h"
+#include "run-command.h"
+#include "refs/refs-advertise.h"
 
 static int config_read;
 static int advertise_unborn;
 static int allow_unborn;
+static struct string_list symref = STRING_LIST_INIT_DUP;
 
 static void ensure_config_read(void)
 {
@@ -81,6 +84,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
 {
 	struct ls_refs_data *data = cb_data;
 	const char *refname_nons = strip_namespace(refname);
+	const char *refname_to_filter = refname_nons;
 
 	strbuf_reset(&data->buf);
 
@@ -90,6 +94,16 @@ static int send_ref(const char *refname, const struct object_id *oid,
 	if (!ref_match(&data->prefixes, refname_nons))
 		return 0;
 
+	if (!strcmp(refname_nons, "HEAD")) {
+		struct string_list_item *item = string_list_lookup(&symref, "HEAD");
+		if (item) {
+			refname_to_filter = (const char *)item->util;
+		}
+	}
+
+	if (filter_advertise_ref(refname_to_filter, oid))
+		return 0;
+
 	if (oid)
 		strbuf_addf(&data->buf, "%s %s", oid_to_hex(oid), refname_nons);
 	else
@@ -121,18 +135,24 @@ static int send_ref(const char *refname, const struct object_id *oid,
 
 static void send_possibly_unborn_head(struct ls_refs_data *data)
 {
+	const char *symref_target;
+	struct string_list_item *item;
 	struct strbuf namespaced = STRBUF_INIT;
 	struct object_id oid;
 	int flag;
 	int oid_is_null;
 
 	strbuf_addf(&namespaced, "%sHEAD", get_git_namespace());
-	if (!resolve_ref_unsafe(namespaced.buf, 0, &oid, &flag))
+	symref_target = resolve_ref_unsafe(namespaced.buf, 0, &oid, &flag);
+	if (!symref_target)
 		return; /* bad ref */
 	oid_is_null = is_null_oid(&oid);
 	if (!oid_is_null ||
-	    (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))
+	    (data->unborn && data->symrefs && (flag & REF_ISSYMREF))) {
+		item = string_list_append(&symref, "HEAD");
+		item->util = xstrdup(strip_namespace(symref_target));
 		send_ref(namespaced.buf, oid_is_null ? NULL : &oid, flag, data);
+	}
 	strbuf_release(&namespaced);
 }
 
@@ -146,6 +166,10 @@ static int ls_refs_config(const char *var, const char *value, void *data)
 	return parse_hide_refs_config(var, value, "uploadpack");
 }
 
+static void clean_refs_advertise_filter(void) {
+	clean_advertise_refs_filter();
+}
+
 int ls_refs(struct repository *r, struct packet_reader *request)
 {
 	struct ls_refs_data data;
@@ -156,6 +180,8 @@ int ls_refs(struct repository *r, struct packet_reader *request)
 
 	ensure_config_read();
 	git_config(ls_refs_config, NULL);
+	create_advertise_refs_filter("ls-refs");
+	atexit(clean_refs_advertise_filter);
 
 	while (packet_reader_read(request) == PACKET_READ_NORMAL) {
 		const char *arg = request->line;
diff --git a/refs/refs-advertise.c b/refs/refs-advertise.c
new file mode 100644
index 00000000000..0dcced78716
--- /dev/null
+++ b/refs/refs-advertise.c
@@ -0,0 +1,246 @@
+#include "../cache.h"
+#include "../config.h"
+#include "../strbuf.h"
+#include "../hook.h"
+#include "../sigchain.h"
+#include "../pkt-line.h"
+#include "../refs.h"
+#include "../run-command.h"
+#include "connect.h"
+#include "ref-cache.h"
+#include "refs-advertise.h"
+
+struct advertise_refs_filter {
+	struct child_process proc;
+	struct packet_reader reader;
+};
+
+static struct advertise_refs_filter *hook_filter = NULL;
+
+void create_advertise_refs_filter(const char *command) {
+	struct advertise_refs_filter *filter;
+	struct child_process *proc;
+	struct packet_reader *reader;
+	const char *hook_path;
+	int command_support = 0;
+	int version = 0;
+	int code;
+
+	if (hook_filter != NULL)
+		return;
+
+	hook_path = find_hook("refs-advertise");
+	if (!hook_path) {
+		return;
+	}
+
+	filter = (struct advertise_refs_filter *) xcalloc (1, sizeof (struct advertise_refs_filter));
+	proc = &filter->proc;
+	reader = &filter->reader;
+
+	child_process_init(proc);
+	strvec_push(&proc->args, hook_path);
+	proc->in = -1;
+	proc->out = -1;
+	proc->trace2_hook_name = "refs-advertise";
+	proc->err = 0;
+
+	code = start_command(proc);
+	if (code)
+		die("can not run hook refs-advertise");
+
+	sigchain_push(SIGPIPE, SIG_IGN);
+
+	/* Version negotiaton */
+	packet_reader_init(reader, proc->out, NULL, 0,
+			   PACKET_READ_CHOMP_NEWLINE |
+			   PACKET_READ_GENTLE_ON_EOF);
+	code = packet_write_fmt_gently(proc->in, "version=1%c%s", '\0', command);
+	if (!code)
+		code = packet_flush_gently(proc->in);
+
+	if (!code)
+		for (;;) {
+			int linelen;
+			enum packet_read_status status;
+
+			status = packet_reader_read(reader);
+			if (status != PACKET_READ_NORMAL) {
+				/* Check whether refs-advertise exited abnormally */
+				if (status == PACKET_READ_EOF)
+					die("can not read version message from hook refs-advertise");
+				break;
+			}
+
+			if (reader->pktlen > 8 && starts_with(reader->line, "version=")) {
+				version = atoi(reader->line + 8);
+				linelen = strlen(reader->line);
+				if (linelen < reader->pktlen) {
+					const char *command_list = reader->line + linelen + 1;
+					if (parse_feature_request(command_list, command)) {
+						command_support = 1;
+					}
+				}
+			}
+		}
+
+	if (code)
+		die("can not read version message from hook refs-advertise");
+
+	switch (version) {
+	case 0:
+		/* fallthrough */
+	case 1:
+		break;
+	default:
+		die(_("hook refs-advertise version '%d' is not supported"), version);
+	}
+
+	sigchain_pop(SIGPIPE);
+
+	if (!command_support) {
+		close(proc->in);
+		close(proc->out);
+		kill(proc->pid, SIGTERM);
+		finish_command_in_signal(proc);
+		free(filter);
+
+		return;
+	}
+
+	hook_filter = filter;
+	return;
+}
+
+void clean_advertise_refs_filter(void) {
+	struct child_process *proc;
+
+	if (!hook_filter) {
+		return;
+	}
+
+	proc = &hook_filter->proc;
+
+	close(proc->in);
+	close(proc->out);
+	kill(proc->pid, SIGTERM);
+	finish_command_in_signal(proc);
+	FREE_AND_NULL(hook_filter);
+}
+
+static int do_filter_advertise_ref(const char *refname, const struct object_id *oid) {
+	struct child_process *proc;
+	struct packet_reader *reader;
+	struct strbuf buf = STRBUF_INIT;
+	char *oid_hex;
+	int code;
+
+	proc = &hook_filter->proc;
+	reader = &hook_filter->reader;
+	if (oid)
+		oid_hex = oid_to_hex(oid);
+	else
+		oid_hex = oid_to_hex(null_oid());
+
+	code = packet_write_fmt_gently(proc->in, "ref %s %s", refname, oid_hex);
+	if (code)
+		die("hook refs-advertise died abnormally");
+
+	code = packet_flush_gently(proc->in);
+	if (code)
+		die("hook refs-advertise died abnormally");
+
+	for (;;) {
+		enum packet_read_status status;
+
+		status = packet_reader_read(reader);
+		if (status != PACKET_READ_NORMAL) {
+			/* Check whether refs-advertise exited abnormally */
+			if (status == PACKET_READ_EOF)
+				die("hook refs-advertise died abnormally");
+			break;
+		}
+
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, reader->line);
+	}
+
+	if (strncmp("ok ref ", buf.buf, 7))
+		return -1;
+
+	if (strcmp(refname, buf.buf + 7))
+		return -1;
+
+	return 0;
+}
+
+int filter_advertise_ref(const char *refname, const struct object_id *oid) {
+	int result;
+
+	if (!hook_filter) {
+		return 0;
+	}
+
+	sigchain_push(SIGPIPE, SIG_IGN);
+	result = do_filter_advertise_ref(refname, oid);
+	sigchain_pop(SIGPIPE);
+
+	return result;
+}
+
+static int do_filter_advertise_object(const struct object_id *oid) {
+	struct child_process *proc;
+	struct packet_reader *reader;
+	struct strbuf buf = STRBUF_INIT;
+	char *oid_hex;
+	int code;
+
+	proc = &hook_filter->proc;
+	reader = &hook_filter->reader;
+	oid_hex = oid_to_hex(oid);
+
+	code = packet_write_fmt_gently(proc->in, "obj %s", oid_hex);
+	if (code)
+		die("hook refs-advertise died abnormally");
+
+	code = packet_flush_gently(proc->in);
+	if (code)
+		die("hook refs-advertise died abnormally");
+
+	for (;;) {
+		enum packet_read_status status;
+
+		status = packet_reader_read(reader);
+		if (status != PACKET_READ_NORMAL) {
+			/* Check whether refs-advertise exited abnormally */
+			if (status == PACKET_READ_EOF)
+				die("hook refs-advertise died abnormally");
+			break;
+		}
+
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, reader->line);
+	}
+
+	if (strncmp("ok obj ", buf.buf, 7))
+		return -1;
+
+	if (strcmp(oid_hex, buf.buf + 7))
+		return -1;
+
+	return 0;
+}
+
+int filter_advertise_object(const struct object_id *oid) {
+	int result;
+
+	if (!hook_filter || !oid) {
+		return 0;
+	}
+
+	sigchain_push(SIGPIPE, SIG_IGN);
+	result = do_filter_advertise_object(oid);
+	sigchain_pop(SIGPIPE);
+
+	return result;
+}
diff --git a/refs/refs-advertise.h b/refs/refs-advertise.h
new file mode 100644
index 00000000000..615469eaddb
--- /dev/null
+++ b/refs/refs-advertise.h
@@ -0,0 +1,11 @@
+#ifndef REFS_REFS_ADVERTISE_H
+#define REFS_REFS_ADVERTISE_H
+
+#include "../hash.h"
+
+void create_advertise_refs_filter(const char *command);
+void clean_advertise_refs_filter(void);
+int filter_advertise_ref(const char *refname, const struct object_id *oid);
+int filter_advertise_object(const struct object_id *oid);
+
+#endif
diff --git a/t/helper/test-refs-advertise.c b/t/helper/test-refs-advertise.c
new file mode 100644
index 00000000000..c6bcf4e681f
--- /dev/null
+++ b/t/helper/test-refs-advertise.c
@@ -0,0 +1,202 @@
+#include "cache.h"
+#include "hash.h"
+#include "config.h"
+#include "connect.h"
+#include "parse-options.h"
+#include "pkt-line.h"
+#include "sigchain.h"
+#include "test-tool.h"
+
+static const char *refs_advertise_usage[] = {
+	"test-tool refs-advertise [<options>...]",
+	NULL
+};
+
+static int can_upload_pack;
+static int can_receive_pack;
+static int can_ls_refs;
+static int die_read_version;
+static int die_write_version;
+static int die_read_first_ref;
+static int die_read_second_ref;
+static int die_filter_refs;
+static int upload_pack;
+static int receive_pack;
+static int ls_refs;
+static int verbose;
+static int version = 1;
+static int first_ref;
+static int second_ref;
+static int hash_size = GIT_SHA1_HEXSZ;
+static struct string_list returns = STRING_LIST_INIT_NODUP;
+
+struct command {
+	struct command *next;
+	const char *error_string;
+	unsigned int skip_update:1,
+		     did_not_exist:1;
+	int index;
+	struct object_id old_oid;
+	struct object_id new_oid;
+	char ref_name[FLEX_ARRAY]; /* more */
+};
+
+static void refs_advertise_verison(struct packet_reader *reader) {
+	int server_version = 0;
+
+	if (die_read_version)
+		die("die with the --die-read-version option");
+
+	for (;;) {
+		int linelen;
+
+		if (packet_reader_read(reader) != PACKET_READ_NORMAL)
+			break;
+
+		/* Ignore version negotiation for version 0 */
+		if (version == 0)
+			continue;
+
+		if (reader->pktlen > 8 && starts_with(reader->line, "version=")) {
+			server_version = atoi(reader->line+8);
+			if (server_version != 1)
+				die("bad protocol version: %d", server_version);
+			linelen = strlen(reader->line);
+			if (linelen < reader->pktlen) {
+				const char *feature_list = reader->line + linelen + 1;
+				if (parse_feature_request(feature_list, "git-upload-pack"))
+					upload_pack = 1;
+				if (parse_feature_request(feature_list, "git-receive-pack"))
+					receive_pack = 1;
+				if (parse_feature_request(feature_list, "ls-refs"))
+					ls_refs = 1;
+			}
+		}
+	}
+
+	if (die_write_version)
+		die("die with the --die-write-version option");
+
+	if (can_upload_pack || can_receive_pack || can_ls_refs)
+		packet_write_fmt(1, "version=%d%c%s%s%s\n",
+				version, '\0',
+				can_upload_pack ? "git-upload-pack ": "",
+				can_receive_pack? "git-receive-pack ": "",
+				can_ls_refs? "ls-refs ": "");
+	else
+		packet_write_fmt(1, "version=%d\n", version);
+
+	packet_flush(1);
+
+	if ((upload_pack && !can_upload_pack) ||
+		(receive_pack && !can_receive_pack) ||
+		(ls_refs && !can_ls_refs)) {
+			exit(0);
+	}
+}
+
+static void refs_advertise_read_refs(struct packet_reader *reader)
+{
+	const char *p;
+	struct strbuf buf = STRBUF_INIT;
+	enum packet_read_status status;
+	int filter_ok = 0;
+
+	if (!first_ref) {
+		if (die_read_first_ref)
+			die("die with the --die-read-first-ref option");
+
+		first_ref = 1;
+	}
+
+	if (first_ref && !second_ref) {
+		if (die_read_second_ref)
+			die("die with the --die-read-second-ref option");
+
+		second_ref = 1;
+	}
+
+	for (;;) {
+		status = packet_reader_read(reader);
+		if (status == PACKET_READ_EOF)
+			exit(0);
+
+		if (status != PACKET_READ_NORMAL)
+			break;
+
+		p = reader->line;
+		strbuf_reset(&buf);
+		strbuf_addstr(&buf, reader->line);
+	}
+
+	p = buf.buf;
+
+	if (unsorted_string_list_has_string(&returns, p)) {
+		filter_ok = 1;
+	}
+
+	// if it's a ref filter request, we response without the commit id
+	if ((buf.len > (hash_size + 1)) && (strncmp("obj ", buf.buf, 4)))
+		strbuf_setlen(&buf, buf.len - (hash_size + 1));
+
+	if (filter_ok) {
+		packet_write_fmt(1, "%s %s\n", "ok", p);
+	} else {
+		packet_write_fmt(1, "%s %s\n", "ng", p);
+	}
+
+	if (die_filter_refs)
+		die("die with the --die-filter-refs option");
+
+	packet_flush(1);
+}
+
+int cmd__refs_advertise(int argc, const char **argv) {
+	int nongit_ok = 0;
+	struct packet_reader reader;
+	const char *value = NULL;
+	struct option options[] = {
+		OPT_BOOL(0, "can-upload-pack", &can_upload_pack,
+			 "support upload-pack command"),
+		OPT_BOOL(0, "can-receive-pack", &can_receive_pack,
+			 "support upload-pack command"),
+		OPT_BOOL(0, "can-ls-refs", &can_ls_refs,
+			 "support ls-refs command"),
+		OPT_BOOL(0, "die-read-version", &die_read_version,
+			 "die when reading version"),
+		OPT_BOOL(0, "die-write-version", &die_write_version,
+			 "die when writing version"),
+		OPT_BOOL(0, "die-read-first-ref", &die_read_first_ref,
+			 "die when reading first reference"),
+		OPT_BOOL(0, "die-read-second-ref", &die_read_second_ref,
+			 "die when reading second reference"),
+		OPT_BOOL(0, "die-filter-refs", &die_filter_refs,
+			 "die when filtering refs"),
+		OPT_STRING_LIST('r', "return", &returns, "ref<SP>$refname<SP>$oid|obj<SP>$oid",
+				"refs or objects that can advertise"),
+		OPT__VERBOSE(&verbose, "be verbose"),
+		OPT_INTEGER('V', "version", &version,
+			    "use this protocol version number"),
+		OPT_END()
+	};
+
+	setup_git_directory_gently(&nongit_ok);
+
+	argc = parse_options(argc, argv, "test-tools", options, refs_advertise_usage, 0);
+	if (argc > 0)
+		usage_msg_opt("Too many arguments.", refs_advertise_usage, options);
+
+	packet_reader_init(&reader, 0, NULL, 0, PACKET_READ_CHOMP_NEWLINE | PACKET_READ_GENTLE_ON_EOF);
+
+	if (!git_config_get_value("extensions.objectformat", &value)) {
+		if (!strcmp(value, "sha256"))
+			hash_size = GIT_SHA256_HEXSZ;
+	}
+
+	refs_advertise_verison(&reader);
+	for (;;) {
+		refs_advertise_read_refs(&reader);
+	}
+
+	return 0;
+}
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index 318fdbab0c3..10795e4eed6 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -69,6 +69,7 @@ static struct test_cmd cmds[] = {
 	{ "regex", cmd__regex },
 	{ "repository", cmd__repository },
 	{ "revision-walking", cmd__revision_walking },
+	{ "refs-advertise", cmd__refs_advertise },
 	{ "run-command", cmd__run_command },
 	{ "scrap-cache-tree", cmd__scrap_cache_tree },
 	{ "serve-v2", cmd__serve_v2 },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index bb799271631..8f651e5cf9e 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -58,6 +58,7 @@ int cmd__reftable(int argc, const char **argv);
 int cmd__regex(int argc, const char **argv);
 int cmd__repository(int argc, const char **argv);
 int cmd__revision_walking(int argc, const char **argv);
+int cmd__refs_advertise(int argc, const char **argv);
 int cmd__run_command(int argc, const char **argv);
 int cmd__scrap_cache_tree(int argc, const char **argv);
 int cmd__serve_v2(int argc, const char **argv);
diff --git a/upload-pack.c b/upload-pack.c
index 09f48317b02..86714986d5f 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -27,6 +27,7 @@
 #include "commit-graph.h"
 #include "commit-reach.h"
 #include "shallow.h"
+#include "refs/refs-advertise.h"
 
 /* Remember to update object flag allocation in object.h */
 #define THEY_HAVE	(1u << 11)
@@ -1118,7 +1119,7 @@ static void receive_needs(struct upload_pack_data *data,
 		}
 
 		o = parse_object(the_repository, &oid_buf);
-		if (!o) {
+		if ((!o) || (filter_advertise_object(&oid_buf))) {
 			packet_writer_error(&data->writer,
 					    "upload-pack: not our ref %s",
 					    oid_to_hex(&oid_buf));
@@ -1164,6 +1165,12 @@ static int mark_our_ref(const char *refname, const char *refname_full,
 		o->flags |= HIDDEN_REF;
 		return 1;
 	}
+
+	if (filter_advertise_ref(refname, oid)) {
+		o->flags |= HIDDEN_REF;
+		return 1;
+	}
+
 	o->flags |= OUR_REF;
 	return 0;
 }
@@ -1183,8 +1190,10 @@ static void format_symref_info(struct strbuf *buf, struct string_list *symref)
 
 	if (!symref->nr)
 		return;
-	for_each_string_list_item(item, symref)
-		strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util);
+	for_each_string_list_item(item, symref) {
+		if (!filter_advertise_ref((char *)item->util, NULL))
+			strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util);
+	}
 }
 
 static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {
@@ -1198,11 +1207,19 @@ static int send_ref(const char *refname, const struct object_id *oid,
 	static const char *capabilities = "multi_ack thin-pack side-band"
 		" side-band-64k ofs-delta shallow deepen-since deepen-not"
 		" deepen-relative no-progress include-tag multi_ack_detailed";
-	const char *refname_nons = strip_namespace(refname);
 	struct object_id peeled;
 	struct upload_pack_data *data = cb_data;
+	const char *refname_nons = strip_namespace(refname);
+	const char *refname_to_filter = refname_nons;
 
-	if (mark_our_ref(refname_nons, refname, oid))
+	if (!strcmp(refname_nons, "HEAD")) {
+		struct string_list_item *item = string_list_lookup(&data->symref, "HEAD");
+		if (item) {
+			refname_to_filter = (const char *)item->util;
+		}
+	}
+
+	if (mark_our_ref(refname_to_filter, refname, oid))
 		return 0;
 
 	if (capabilities) {
@@ -1342,12 +1359,19 @@ static void get_upload_pack_config(struct upload_pack_data *data)
 	git_protected_config(upload_pack_protected_config, data);
 }
 
+static void clean_refs_advertise_filter(void) {
+	clean_advertise_refs_filter();
+}
+
 void upload_pack(const int advertise_refs, const int stateless_rpc,
 		 const int timeout)
 {
 	struct packet_reader reader;
 	struct upload_pack_data data;
 
+	create_advertise_refs_filter("git-upload-pack");
+	atexit(clean_refs_advertise_filter);
+
 	upload_pack_data_init(&data);
 	get_upload_pack_config(&data);
 
@@ -1421,7 +1445,7 @@ static int parse_want(struct packet_writer *writer, const char *line,
 		else
 			o = parse_object(the_repository, &oid);
 
-		if (!o) {
+		if ((!o) || (filter_advertise_object(&oid))) {
 			packet_writer_error(writer,
 					    "upload-pack: not our ref %s",
 					    oid_to_hex(&oid));
@@ -1453,7 +1477,8 @@ static int parse_want_ref(struct packet_writer *writer, const char *line,
 
 		strbuf_addf(&refname, "%s%s", get_git_namespace(), refname_nons);
 		if (ref_is_hidden(refname_nons, refname.buf) ||
-		    read_ref(refname.buf, &oid)) {
+		    read_ref(refname.buf, &oid) ||
+			filter_advertise_ref(refname_nons, &oid)) {
 			packet_writer_error(writer, "unknown ref %s", refname_nons);
 			die("unknown ref %s", refname_nons);
 		}
@@ -1701,6 +1726,9 @@ int upload_pack_v2(struct repository *r, struct packet_reader *request)
 
 	clear_object_flags(ALL_FLAGS);
 
+	create_advertise_refs_filter("git-upload-pack");
+	atexit(clean_refs_advertise_filter);
+
 	upload_pack_data_init(&data);
 	data.use_sideband = LARGE_PACKET_MAX;
 	get_upload_pack_config(&data);
-- 
gitgitgadget


  reply	other threads:[~2022-08-03 16:17 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-03 16:17 [PATCH 0/3] refs-advertise: add hook to filter advertised refs Sun Chao via GitGitGadget
2022-08-03 16:17 ` Sun Chao via GitGitGadget [this message]
2022-08-03 16:17 ` [PATCH 2/3] t1419: add test cases for refs-advertise hook Sun Chao via GitGitGadget
2022-08-03 16:17 ` [PATCH 3/3] doc: add documentation for the " Sun Chao via GitGitGadget
2022-08-03 20:27 ` [PATCH 0/3] refs-advertise: add hook to filter advertised refs Junio C Hamano
2022-08-04  8:27   ` 孙超
2022-08-10  1:06 ` Jiang Xin
2022-08-10 13:09   ` 孙超
2022-08-15  0:54 ` [PATCH v2 0/3] hide-refs: add hook to force hide refs Sun Chao via GitGitGadget
2022-08-15  0:54   ` [PATCH v2 1/3] " Sun Chao via GitGitGadget
2022-08-15  0:54   ` [PATCH v2 2/3] t1419: add test cases for hide-refs hook Sun Chao via GitGitGadget
2022-08-15  0:54   ` [PATCH v2 3/3] doc: add documentation for the " Sun Chao via GitGitGadget
2022-08-15  4:12     ` Eric Sunshine
2022-08-15 14:49       ` 孙超
2022-08-15 16:02         ` Junio C Hamano
2022-08-15 14:56   ` [PATCH v3 0/3] hide-refs: add hook to force hide refs Sun Chao via GitGitGadget
2022-08-15 14:56     ` [PATCH v3 1/3] " Sun Chao via GitGitGadget
2022-08-15 14:56     ` [PATCH v3 2/3] t1419: add test cases for hide-refs hook Sun Chao via GitGitGadget
2022-08-15 14:56     ` [PATCH v3 3/3] doc: add documentation for the " Sun Chao via GitGitGadget
2022-08-15 15:01     ` [PATCH v4 0/3] hide-refs: add hook to force hide refs Sun Chao via GitGitGadget
2022-08-15 15:01       ` [PATCH v4 1/3] " Sun Chao via GitGitGadget
2022-08-15 18:18         ` Junio C Hamano
2022-08-16 11:22           ` 孙超
2022-08-18 18:51         ` Calvin Wan
2022-08-19 15:30           ` 孙超
2022-08-15 15:01       ` [PATCH v4 2/3] t1419: add test cases for hide-refs hook Sun Chao via GitGitGadget
2022-08-15 15:01       ` [PATCH v4 3/3] doc: add documentation for the " Sun Chao via GitGitGadget
2022-09-09 15:06       ` [PATCH v5 0/5] hiderefs: add hide-refs hook to hide refs dynamically Sun Chao via GitGitGadget
2022-09-09 15:06         ` [PATCH v5 1/5] " Sun Chao via GitGitGadget
2022-09-13 17:01           ` Junio C Hamano
2022-09-16 17:52             ` Junio C Hamano
2022-09-17  8:14               ` 孙超
2022-09-09 15:06         ` [PATCH v5 2/5] hiderefs: use new flag to mark force hidden refs Sun Chao via GitGitGadget
2022-09-09 15:06         ` [PATCH v5 3/5] hiderefs: hornor hide flags in wire protocol V2 Sun Chao via GitGitGadget
2022-09-09 15:06         ` [PATCH v5 4/5] test: add test cases for hide-refs hook Sun Chao via GitGitGadget
2022-09-09 15:06         ` [PATCH v5 5/5] doc: add documentation for the " Sun Chao via GitGitGadget
2022-09-20  8:22         ` [PATCH v6 0/5] hiderefs: add hide-refs hook to hide refs dynamically Sun Chao via GitGitGadget
2022-09-20  8:22           ` [PATCH v6 1/5] " Sun Chao via GitGitGadget
2022-09-20  8:22           ` [PATCH v6 2/5] hiderefs: use a new flag to mark force hidden refs Sun Chao via GitGitGadget
2022-09-20  8:22           ` [PATCH v6 3/5] hiderefs: hornor hide flags in wire protocol V2 Sun Chao via GitGitGadget
2022-09-20  8:22           ` [PATCH v6 4/5] test: add test cases for hide-refs hook Sun Chao via GitGitGadget
2022-09-20  8:22           ` [PATCH v6 5/5] doc: add documentation for the " Sun Chao via GitGitGadget

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=b4b5ce5a361caf08ea15a8066f6cac5b255a527d.1659543457.git.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=16657101987@163.com \
    --cc=git@vger.kernel.org \
    --cc=sunchao9@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

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 read-only IMAP folder(s) and NNTP newsgroup(s).