summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/config/uploadpack.txt12
-rwxr-xr-xt/t5616-partial-clone.sh24
-rw-r--r--upload-pack.c86
3 files changed, 122 insertions, 0 deletions
diff --git a/Documentation/config/uploadpack.txt b/Documentation/config/uploadpack.txt
index ed1c835695..fffe8ac648 100644
--- a/Documentation/config/uploadpack.txt
+++ b/Documentation/config/uploadpack.txt
@@ -57,6 +57,18 @@ uploadpack.allowFilter::
If this option is set, `upload-pack` will support partial
clone and partial fetch object filtering.
+uploadpackfilter.allow::
+ Provides a default value for unspecified object filters (see: the
+ below configuration variable).
+ Defaults to `true`.
+
+uploadpackfilter.<filter>.allow::
+ Explicitly allow or ban the object filter corresponding to
+ `<filter>`, where `<filter>` may be one of: `blob:none`,
+ `blob:limit`, `tree`, `sparse:oid`, or `combine`. If using
+ combined filters, both `combine` and all of the nested filter
+ kinds must be allowed. Defaults to `uploadpackfilter.allow`.
+
uploadpack.allowRefInWant::
If this option is set, `upload-pack` will support the `ref-in-want`
feature of the protocol version 2 `fetch` command. This feature
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 8a27452a51..1fff4ff3a9 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -235,6 +235,30 @@ test_expect_success 'implicitly construct combine: filter with repeated flags' '
test_cmp unique_types.expected unique_types.actual
'
+test_expect_success 'upload-pack fails banned object filters' '
+ test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
+ test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
+ "file://$(pwd)/srv.bare" pc3 2>err &&
+ grep "filter '\''blob:none'\'' not supported" err
+'
+
+test_expect_success 'upload-pack fails banned combine object filters' '
+ test_config -C srv.bare uploadpackfilter.allow false &&
+ test_config -C srv.bare uploadpackfilter.combine.allow true &&
+ test_config -C srv.bare uploadpackfilter.tree.allow true &&
+ test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
+ test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
+ --filter=blob:none "file://$(pwd)/srv.bare" pc3 2>err &&
+ grep "filter '\''blob:none'\'' not supported" err
+'
+
+test_expect_success 'upload-pack fails banned object filters with fallback' '
+ test_config -C srv.bare uploadpackfilter.allow false &&
+ test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
+ "file://$(pwd)/srv.bare" pc3 2>err &&
+ grep "filter '\''blob:none'\'' not supported" err
+'
+
test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
rm -rf src dst &&
git init src &&
diff --git a/upload-pack.c b/upload-pack.c
index 39d0cf00be..5a9d767a12 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -88,6 +88,7 @@ struct upload_pack_data {
enum allow_uor allow_uor;
struct list_objects_filter_options filter_options;
+ struct string_list allowed_filters;
struct packet_writer writer;
@@ -103,6 +104,7 @@ struct upload_pack_data {
unsigned no_progress : 1;
unsigned use_include_tag : 1;
unsigned allow_filter : 1;
+ unsigned allow_filter_fallback : 1;
unsigned done : 1; /* v2 only */
unsigned allow_ref_in_want : 1; /* v2 only */
@@ -120,6 +122,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
struct string_list deepen_not = STRING_LIST_INIT_DUP;
struct string_list uri_protocols = STRING_LIST_INIT_DUP;
struct object_array extra_edge_obj = OBJECT_ARRAY_INIT;
+ struct string_list allowed_filters = STRING_LIST_INIT_DUP;
memset(data, 0, sizeof(*data));
data->symref = symref;
@@ -131,6 +134,8 @@ static void upload_pack_data_init(struct upload_pack_data *data)
data->deepen_not = deepen_not;
data->uri_protocols = uri_protocols;
data->extra_edge_obj = extra_edge_obj;
+ data->allowed_filters = allowed_filters;
+ data->allow_filter_fallback = 1;
packet_writer_init(&data->writer, 1);
data->keepalive = 5;
@@ -147,6 +152,7 @@ static void upload_pack_data_clear(struct upload_pack_data *data)
string_list_clear(&data->deepen_not, 0);
object_array_clear(&data->extra_edge_obj);
list_objects_filter_release(&data->filter_options);
+ string_list_clear(&data->allowed_filters, 1);
free((char *)data->pack_objects_hook);
}
@@ -983,6 +989,56 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not,
return 0;
}
+NORETURN __attribute__((format(printf,2,3)))
+static void send_err_and_die(struct upload_pack_data *data,
+ const char *fmt, ...)
+{
+ struct strbuf buf = STRBUF_INIT;
+ va_list ap;
+
+ va_start(ap, fmt);
+ strbuf_vaddf(&buf, fmt, ap);
+ va_end(ap);
+
+ packet_writer_error(&data->writer, "%s", buf.buf);
+ die("%s", buf.buf);
+}
+
+static void check_one_filter(struct upload_pack_data *data,
+ struct list_objects_filter_options *opts)
+{
+ const char *key = list_object_filter_config_name(opts->choice);
+ struct string_list_item *item = string_list_lookup(&data->allowed_filters,
+ key);
+ int allowed;
+
+ if (item)
+ allowed = (intptr_t)item->util;
+ else
+ allowed = data->allow_filter_fallback;
+
+ if (!allowed)
+ send_err_and_die(data, "filter '%s' not supported", key);
+}
+
+static void check_filter_recurse(struct upload_pack_data *data,
+ struct list_objects_filter_options *opts)
+{
+ size_t i;
+
+ check_one_filter(data, opts);
+ if (opts->choice != LOFC_COMBINE)
+ return;
+
+ for (i = 0; i < opts->sub_nr; i++)
+ check_filter_recurse(data, &opts->sub[i]);
+}
+
+static void die_if_using_banned_filter(struct upload_pack_data *data)
+{
+ check_filter_recurse(data, &data->filter_options);
+}
+
static void receive_needs(struct upload_pack_data *data,
struct packet_reader *reader)
{
@@ -1013,6 +1069,7 @@ static void receive_needs(struct upload_pack_data *data,
die("git upload-pack: filtering capability not negotiated");
list_objects_filter_die_if_populated(&data->filter_options);
parse_list_objects_filter(&data->filter_options, arg);
+ die_if_using_banned_filter(data);
continue;
}
@@ -1169,6 +1226,32 @@ static int find_symref(const char *refname, const struct object_id *oid,
return 0;
}
+static int parse_object_filter_config(const char *var, const char *value,
+ struct upload_pack_data *data)
+{
+ struct strbuf buf = STRBUF_INIT;
+ const char *sub, *key;
+ size_t sub_len;
+
+ if (parse_config_key(var, "uploadpackfilter", &sub, &sub_len, &key))
+ return 0;
+
+ if (!sub) {
+ if (!strcmp(key, "allow"))
+ data->allow_filter_fallback = git_config_bool(var, value);
+ return 0;
+ }
+
+ strbuf_add(&buf, sub, sub_len);
+
+ if (!strcmp(key, "allow"))
+ string_list_insert(&data->allowed_filters, buf.buf)->util =
+ (void *)(intptr_t)git_config_bool(var, value);
+
+ strbuf_release(&buf);
+ return 0;
+}
+
static int upload_pack_config(const char *var, const char *value, void *cb_data)
{
struct upload_pack_data *data = cb_data;
@@ -1208,6 +1291,8 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data)
return git_config_string(&data->pack_objects_hook, var, value);
}
+ parse_object_filter_config(var, value, data);
+
return parse_hide_refs_config(var, value, "uploadpack");
}
@@ -1388,6 +1473,7 @@ static void process_args(struct packet_reader *request,
if (data->allow_filter && skip_prefix(arg, "filter ", &p)) {
list_objects_filter_die_if_populated(&data->filter_options);
parse_list_objects_filter(&data->filter_options, p);
+ die_if_using_banned_filter(data);
continue;
}