@matheusbsbs / matheus-bagliano / commits / 6dd3456a8c
Fork 0

upload-pack.c: allow banning certain object filter(s)

Git clients may ask the server for a partial set of objects, where the set of objects being requested is refined by one or more object filters. Server administrators can configure 'git upload-pack' to allow or ban these filters by setting the 'uploadpack.allowFilter' variable to 'true' or 'false', respectively. However, administrators using bitmaps may wish to allow certain kinds of object filters, but ban others. Specifically, they may wish to allow object filters that can be optimized by the use of bitmaps, while rejecting other object filters which aren't and represent a perceived performance degradation (as well as an increased load factor on the server). Allow configuring 'git upload-pack' to support object filters on a case-by-case basis by introducing two new configuration variables: - 'uploadpackfilter.allow' - 'uploadpackfilter.<kind>.allow' where '<kind>' may be one of 'blobNone', 'blobLimit', 'tree', and so on. Setting the second configuration variable for any valid value of '<kind>' explicitly allows or disallows restricting that kind of object filter. If a client requests the object filter <kind> and the respective configuration value is not set, 'git upload-pack' will default to the value of 'uploadpackfilter.allow', which itself defaults to 'true' to maintain backwards compatibility. Note that this differs from 'uploadpack.allowfilter', which controls whether or not the 'filter' capability is advertised. Helped-by: Jeff King <peff@peff.net> Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Taylor Blau committed Aug 3, 2020 at 14:00 UTC 6dd3456a8ccbad6a0a6a31bc7192025617114243
3 files changed +122
Documentation/config/uploadpack.txt
+12
@@ -57,6 +57,18 @@ uploadpack.allowFilter::
57 If this option is set, `upload-pack` will support partial
58 clone and partial fetch object filtering.
59
60 +uploadpackfilter.allow::
61 + Provides a default value for unspecified object filters (see: the
62 + below configuration variable).
63 + Defaults to `true`.
64 +
65 +uploadpackfilter.<filter>.allow::
66 + Explicitly allow or ban the object filter corresponding to
67 + `<filter>`, where `<filter>` may be one of: `blob:none`,
68 + `blob:limit`, `tree`, `sparse:oid`, or `combine`. If using
69 + combined filters, both `combine` and all of the nested filter
70 + kinds must be allowed. Defaults to `uploadpackfilter.allow`.
71 +
72 uploadpack.allowRefInWant::
73 If this option is set, `upload-pack` will support the `ref-in-want`
74 feature of the protocol version 2 `fetch` command. This feature
t/t5616-partial-clone.sh
+24
@@ -235,6 +235,30 @@ test_expect_success 'implicitly construct combine: filter with repeated flags' '
235 test_cmp unique_types.expected unique_types.actual
236 '
237
238 +test_expect_success 'upload-pack fails banned object filters' '
239 + test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
240 + test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
241 + "file://$(pwd)/srv.bare" pc3 2>err &&
242 + grep "filter '\''blob:none'\'' not supported" err
243 +'
244 +
245 +test_expect_success 'upload-pack fails banned combine object filters' '
246 + test_config -C srv.bare uploadpackfilter.allow false &&
247 + test_config -C srv.bare uploadpackfilter.combine.allow true &&
248 + test_config -C srv.bare uploadpackfilter.tree.allow true &&
249 + test_config -C srv.bare uploadpackfilter.blob:none.allow false &&
250 + test_must_fail ok=sigpipe git clone --no-checkout --filter=tree:1 \
251 + --filter=blob:none "file://$(pwd)/srv.bare" pc3 2>err &&
252 + grep "filter '\''blob:none'\'' not supported" err
253 +'
254 +
255 +test_expect_success 'upload-pack fails banned object filters with fallback' '
256 + test_config -C srv.bare uploadpackfilter.allow false &&
257 + test_must_fail ok=sigpipe git clone --no-checkout --filter=blob:none \
258 + "file://$(pwd)/srv.bare" pc3 2>err &&
259 + grep "filter '\''blob:none'\'' not supported" err
260 +'
261 +
262 test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
263 rm -rf src dst &&
264 git init src &&
upload-pack.c
+86
@@ -88,6 +88,7 @@ struct upload_pack_data {
88 enum allow_uor allow_uor;
89
90 struct list_objects_filter_options filter_options;
91 + struct string_list allowed_filters;
92
93 struct packet_writer writer;
94
@@ -103,6 +104,7 @@ struct upload_pack_data {
104 unsigned no_progress : 1;
105 unsigned use_include_tag : 1;
106 unsigned allow_filter : 1;
107 + unsigned allow_filter_fallback : 1;
108
109 unsigned done : 1; /* v2 only */
110 unsigned allow_ref_in_want : 1; /* v2 only */
@@ -120,6 +122,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
122 struct string_list deepen_not = STRING_LIST_INIT_DUP;
123 struct string_list uri_protocols = STRING_LIST_INIT_DUP;
124 struct object_array extra_edge_obj = OBJECT_ARRAY_INIT;
125 + struct string_list allowed_filters = STRING_LIST_INIT_DUP;
126
127 memset(data, 0, sizeof(*data));
128 data->symref = symref;
@@ -131,6 +134,8 @@ static void upload_pack_data_init(struct upload_pack_data *data)
134 data->deepen_not = deepen_not;
135 data->uri_protocols = uri_protocols;
136 data->extra_edge_obj = extra_edge_obj;
137 + data->allowed_filters = allowed_filters;
138 + data->allow_filter_fallback = 1;
139 packet_writer_init(&data->writer, 1);
140
141 data->keepalive = 5;
@@ -147,6 +152,7 @@ static void upload_pack_data_clear(struct upload_pack_data *data)
152 string_list_clear(&data->deepen_not, 0);
153 object_array_clear(&data->extra_edge_obj);
154 list_objects_filter_release(&data->filter_options);
155 + string_list_clear(&data->allowed_filters, 1);
156
157 free((char *)data->pack_objects_hook);
158 }
@@ -983,6 +989,56 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not,
989 return 0;
990 }
991
992 +NORETURN __attribute__((format(printf,2,3)))
993 +static void send_err_and_die(struct upload_pack_data *data,
994 + const char *fmt, ...)
995 +{
996 + struct strbuf buf = STRBUF_INIT;
997 + va_list ap;
998 +
999 + va_start(ap, fmt);
1000 + strbuf_vaddf(&buf, fmt, ap);
1001 + va_end(ap);
1002 +
1003 + packet_writer_error(&data->writer, "%s", buf.buf);
1004 + die("%s", buf.buf);
1005 +}
1006 +
1007 +static void check_one_filter(struct upload_pack_data *data,
1008 + struct list_objects_filter_options *opts)
1009 +{
1010 + const char *key = list_object_filter_config_name(opts->choice);
1011 + struct string_list_item *item = string_list_lookup(&data->allowed_filters,
1012 + key);
1013 + int allowed;
1014 +
1015 + if (item)
1016 + allowed = (intptr_t)item->util;
1017 + else
1018 + allowed = data->allow_filter_fallback;
1019 +
1020 + if (!allowed)
1021 + send_err_and_die(data, "filter '%s' not supported", key);
1022 +}
1023 +
1024 +static void check_filter_recurse(struct upload_pack_data *data,
1025 + struct list_objects_filter_options *opts)
1026 +{
1027 + size_t i;
1028 +
1029 + check_one_filter(data, opts);
1030 + if (opts->choice != LOFC_COMBINE)
1031 + return;
1032 +
1033 + for (i = 0; i < opts->sub_nr; i++)
1034 + check_filter_recurse(data, &opts->sub[i]);
1035 +}
1036 +
1037 +static void die_if_using_banned_filter(struct upload_pack_data *data)
1038 +{
1039 + check_filter_recurse(data, &data->filter_options);
1040 +}
1041 +
1042 static void receive_needs(struct upload_pack_data *data,
1043 struct packet_reader *reader)
1044 {
@@ -1013,6 +1069,7 @@ static void receive_needs(struct upload_pack_data *data,
1069 die("git upload-pack: filtering capability not negotiated");
1070 list_objects_filter_die_if_populated(&data->filter_options);
1071 parse_list_objects_filter(&data->filter_options, arg);
1072 + die_if_using_banned_filter(data);
1073 continue;
1074 }
1075
@@ -1169,6 +1226,32 @@ static int find_symref(const char *refname, const struct object_id *oid,
1226 return 0;
1227 }
1228
1229 +static int parse_object_filter_config(const char *var, const char *value,
1230 + struct upload_pack_data *data)
1231 +{
1232 + struct strbuf buf = STRBUF_INIT;
1233 + const char *sub, *key;
1234 + size_t sub_len;
1235 +
1236 + if (parse_config_key(var, "uploadpackfilter", &sub, &sub_len, &key))
1237 + return 0;
1238 +
1239 + if (!sub) {
1240 + if (!strcmp(key, "allow"))
1241 + data->allow_filter_fallback = git_config_bool(var, value);
1242 + return 0;
1243 + }
1244 +
1245 + strbuf_add(&buf, sub, sub_len);
1246 +
1247 + if (!strcmp(key, "allow"))
1248 + string_list_insert(&data->allowed_filters, buf.buf)->util =
1249 + (void *)(intptr_t)git_config_bool(var, value);
1250 +
1251 + strbuf_release(&buf);
1252 + return 0;
1253 +}
1254 +
1255 static int upload_pack_config(const char *var, const char *value, void *cb_data)
1256 {
1257 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)
1291 return git_config_string(&data->pack_objects_hook, var, value);
1292 }
1293
1294 + parse_object_filter_config(var, value, data);
1295 +
1296 return parse_hide_refs_config(var, value, "uploadpack");
1297 }
1298
@@ -1388,6 +1473,7 @@ static void process_args(struct packet_reader *request,
1473 if (data->allow_filter && skip_prefix(arg, "filter ", &p)) {
1474 list_objects_filter_die_if_populated(&data->filter_options);
1475 parse_list_objects_filter(&data->filter_options, p);
1476 + die_if_using_banned_filter(data);
1477 continue;
1478 }
1479