Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.kernel.org/pub/scm/git/git.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/config/uploadpack.txt18
-rw-r--r--list-objects-filter-options.c23
-rw-r--r--list-objects-filter-options.h6
-rwxr-xr-xt/t5616-partial-clone.sh33
-rw-r--r--upload-pack.c104
5 files changed, 184 insertions, 0 deletions
diff --git a/Documentation/config/uploadpack.txt b/Documentation/config/uploadpack.txt
index ed1c835695..ee7b3ac94f 100644
--- a/Documentation/config/uploadpack.txt
+++ b/Documentation/config/uploadpack.txt
@@ -57,6 +57,24 @@ 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`.
+
+uploadpackfilter.tree.maxDepth::
+ Only allow `--filter=tree=<n>` when `n` is no more than the value of
+ `uploadpackfilter.tree.maxDepth`. If set, this also implies
+ `uploadpackfilter.tree.allow=true`, unless this configuration
+ variable had already been set. Has no effect if unset.
+
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/list-objects-filter-options.c b/list-objects-filter-options.c
index 3667766f29..b66314560a 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -15,6 +15,29 @@ static int parse_combine_filter(
const char *arg,
struct strbuf *errbuf);
+const char *list_object_filter_config_name(enum list_objects_filter_choice c)
+{
+ switch (c) {
+ case LOFC_DISABLED:
+ /* we have no name for "no filter at all" */
+ break;
+ case LOFC_BLOB_NONE:
+ return "blob:none";
+ case LOFC_BLOB_LIMIT:
+ return "blob:limit";
+ case LOFC_TREE_DEPTH:
+ return "tree";
+ case LOFC_SPARSE_OID:
+ return "sparse:oid";
+ case LOFC_COMBINE:
+ return "combine";
+ case LOFC__COUNT:
+ /* not a real filter type; just the count of all filters */
+ break;
+ }
+ BUG("list_object_filter_choice_name: invalid argument '%d'", c);
+}
+
/*
* Parse value of the argument to the "filter" keyword.
* On the command line this looks like:
diff --git a/list-objects-filter-options.h b/list-objects-filter-options.h
index 73fffa4ad7..01767c3c96 100644
--- a/list-objects-filter-options.h
+++ b/list-objects-filter-options.h
@@ -17,6 +17,12 @@ enum list_objects_filter_choice {
LOFC__COUNT /* must be last */
};
+/*
+ * Returns a configuration key suitable for describing the given object filter,
+ * e.g.: "blob:none", "combine", etc.
+ */
+const char *list_object_filter_config_name(enum list_objects_filter_choice c);
+
struct list_objects_filter_options {
/*
* 'filter_spec' is the raw argument value given on the command line
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 37de0afb02..8827c2ed18 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -235,6 +235,39 @@ 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 &&
+ test_i18ngrep "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 &&
+ test_i18ngrep "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 &&
+ test_i18ngrep "filter '\''blob:none'\'' not supported" err
+'
+
+test_expect_success 'upload-pack limits tree depth filters' '
+ test_config -C srv.bare uploadpackfilter.allow false &&
+ test_config -C srv.bare uploadpackfilter.tree.allow true &&
+ test_config -C srv.bare uploadpackfilter.tree.maxDepth 0 &&
+ test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
+ "file://$(pwd)/srv.bare" pc3 2>err &&
+ test_i18ngrep "tree filter allows max depth 0, but got 1" 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 d087113d23..80ad9a38d8 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,8 @@ struct upload_pack_data {
unsigned no_progress : 1;
unsigned use_include_tag : 1;
unsigned allow_filter : 1;
+ unsigned allow_filter_fallback : 1;
+ unsigned long tree_filter_max_depth;
unsigned done : 1; /* v2 only */
unsigned allow_ref_in_want : 1; /* v2 only */
@@ -120,6 +123,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 +135,9 @@ 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;
+ data->tree_filter_max_depth = ULONG_MAX;
packet_writer_init(&data->writer, 1);
data->keepalive = 5;
@@ -147,6 +154,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 +991,63 @@ 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);
+
+ if (opts->choice == LOFC_TREE_DEPTH &&
+ opts->tree_exclude_depth > data->tree_filter_max_depth)
+ send_err_and_die(data,
+ "tree filter allows max depth %lu, but got %lu",
+ data->tree_filter_max_depth,
+ opts->tree_exclude_depth);
+}
+
+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 +1078,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;
}
@@ -1170,6 +1236,41 @@ 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);
+ else if (!strcmp(buf.buf, "tree") && !strcmp(key, "maxdepth")) {
+ if (!value) {
+ strbuf_release(&buf);
+ return config_error_nonbool(var);
+ }
+ string_list_insert(&data->allowed_filters, buf.buf)->util =
+ (void *)(intptr_t)1;
+ data->tree_filter_max_depth = git_config_ulong(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;
@@ -1209,6 +1310,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");
}
@@ -1389,6 +1492,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;
}