Skip to content
50 changes: 50 additions & 0 deletions bundle-uri.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "remote.h"
#include "trace2.h"
#include "odb.h"
#include "transport.h"
#include "url.h"

static struct {
enum bundle_list_heuristic heuristic;
Expand Down Expand Up @@ -890,11 +892,59 @@ int fetch_bundle_uri(struct repository *r, const char *uri,
return result;
}

/* protocol of 'uri', or "file" if it has none (bare/UNC/relative path) */
static void bundle_uri_protocol(const char *uri, struct strbuf *out)
{
const char *p = uri;

while (is_urlschemechar(p == uri, *p))
p++;
strbuf_reset(out);
if (p > uri && starts_with(p, "://"))
strbuf_add(out, uri, p - uri);
else
strbuf_addstr(out, "file");
}

/* Drop advertised URIs whose protocol is not allowed (see protocol.*.allow). */
static void sanitize_bundle_list(struct bundle_list *list)
{
struct remote_bundle_info **skipped;
size_t nr = 0, i;
struct remote_bundle_info *info;
struct hashmap_iter iter;
struct strbuf proto = STRBUF_INIT;

ALLOC_ARRAY(skipped, hashmap_get_size(&list->bundles));
hashmap_for_each_entry(&list->bundles, &iter, info, ent) {
if (!info->uri)
continue;
bundle_uri_protocol(info->uri, &proto);
/* advertised URIs are not user-provided */
if (!is_transport_allowed(proto.buf, 0)) {
warning(_("skipping bundle URI '%s': protocol '%s' "
"is not allowed"), info->uri, proto.buf);
skipped[nr++] = info;
}
}
strbuf_release(&proto);

for (i = 0; i < nr; i++) {
hashmap_remove(&list->bundles, &skipped[i]->ent, NULL);
clear_remote_bundle_info(skipped[i], NULL);
free(skipped[i]);
}

free(skipped);
}

int fetch_bundle_list(struct repository *r, struct bundle_list *list)
{
int result;
struct bundle_list global_list;

sanitize_bundle_list(list);

/*
* If the creationToken heuristic is used, then the URIs
* advertised by 'list' are not nested lists and instead
Expand Down
2 changes: 1 addition & 1 deletion odb/source-files.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ static int odb_source_files_freshen_object(struct odb_source *source,
}

static int odb_source_files_write_object(struct odb_source *source,
const void *buf, unsigned long len,
const void *buf, size_t len,
enum object_type type,
struct object_id *oid,
struct object_id *compat_oid,
Expand Down
2 changes: 1 addition & 1 deletion odb/source-inmemory.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ static int odb_source_inmemory_count_objects(struct odb_source *source,
}

static int odb_source_inmemory_write_object(struct odb_source *source,
const void *buf, unsigned long len,
const void *buf, size_t len,
enum object_type type,
struct object_id *oid,
struct object_id *compat_oid UNUSED,
Expand Down
2 changes: 1 addition & 1 deletion odb/source-loose.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ static int odb_source_loose_freshen_object(struct odb_source *source,
}

static int odb_source_loose_write_object(struct odb_source *source,
const void *buf, unsigned long len,
const void *buf, size_t len,
enum object_type type, struct object_id *oid,
struct object_id *compat_oid_in,
enum odb_write_object_flags flags)
Expand Down
2 changes: 1 addition & 1 deletion odb/source.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ struct odb_source {
* return 0 on success, a negative error code otherwise.
*/
int (*write_object)(struct odb_source *source,
const void *buf, unsigned long len,
const void *buf, size_t len,
enum object_type type,
struct object_id *oid,
struct object_id *compat_oid,
Expand Down
56 changes: 56 additions & 0 deletions t/lib-bundle-uri-protocol.sh
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,59 @@ test_expect_success "test bundle-uri with $BUNDLE_URI_PROTOCOL:// using protocol
>actual &&
test_cmp_config_output expect actual
'

# Advertised bundle URIs are subject to protocol.*.allow; "file" (and bare or
# UNC paths) is denied by default, so such a URI must be skipped, not fetched.
advertise_uri () {
test_config -C "$BUNDLE_URI_PARENT" bundle.version 1 &&
test_config -C "$BUNDLE_URI_PARENT" bundle.mode all &&
test_config -C "$BUNDLE_URI_PARENT" bundle.payload.uri "$1"
}

ignores_advertised_uri () {
rm -rf victim &&
advertise_uri "$1" &&
git -c transfer.bundleURI=true -c protocol.version=2 \
clone "$BUNDLE_URI_REPO_URI" victim &&
git -C victim for-each-ref refs/bundles/ >refs &&
test_must_be_empty refs
}

test_expect_success "create bundle to advertise" '
git -C "$BUNDLE_URI_PARENT" bundle create "$PWD/payload.bundle" main
'

test_expect_success "ignore non-HTTP(S) bundle URI with $BUNDLE_URI_PROTOCOL://" '
ignores_advertised_uri "$PWD/payload.bundle" &&
ignores_advertised_uri "file://$PWD/payload.bundle"
'

test_expect_success "protocol.file.allow=always honors file bundle URI with $BUNDLE_URI_PROTOCOL://" '
rm -rf victim &&
advertise_uri "$PWD/payload.bundle" &&
git -c transfer.bundleURI=true -c protocol.version=2 \
-c protocol.file.allow=always \
clone "$BUNDLE_URI_REPO_URI" victim &&
git -C victim rev-parse --verify refs/bundles/heads/main
'

# same path via a UNC administrative share (cf. t5580-unc-paths.sh)
if test_have_prereq CYGWIN
then
UNCPATH="$(cygpath -aw .)"
elif test_have_prereq MINGW
then
UNCPATH="$(pwd)"
fi
case "$UNCPATH" in
[A-Za-z]:*)
WITHOUTDRIVE="${UNCPATH#?:}"
UNCPATH="//localhost/${UNCPATH%%:*}\$$WITHOUTDRIVE"
test -d "$UNCPATH" && test_set_prereq ADMIN_UNC
;;
esac

test_expect_success ADMIN_UNC "ignore UNC bundle URI with $BUNDLE_URI_PROTOCOL://" '
ignores_advertised_uri "$UNCPATH/payload.bundle" &&
ignores_advertised_uri "file://$UNCPATH/payload.bundle"
'
8 changes: 4 additions & 4 deletions t/t1007-hash-object.sh
Original file line number Diff line number Diff line change
Expand Up @@ -261,23 +261,23 @@ test_expect_success '--stdin outside of repository (uses default hash)' '
test_cmp expect actual
'

test_expect_success EXPENSIVE,SIZE_T_IS_64BIT,!LONG_IS_64BIT \
test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \
'files over 4GB hash literally' '
test-tool genzeros $((5*1024*1024*1024)) >big &&
test_oid large5GB >expect &&
git hash-object --stdin --literally <big >actual &&
test_cmp expect actual
'

test_expect_success EXPENSIVE,SIZE_T_IS_64BIT,!LONG_IS_64BIT \
test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \
'files over 4GB hash correctly via --stdin' '
{ test -f big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&
test_oid large5GB >expect &&
git hash-object --stdin <big >actual &&
test_cmp expect actual
'

test_expect_success EXPENSIVE,SIZE_T_IS_64BIT,!LONG_IS_64BIT \
test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \
'files over 4GB hash correctly' '
{ test -f big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&
test_oid large5GB >expect &&
Expand All @@ -287,7 +287,7 @@ test_expect_success EXPENSIVE,SIZE_T_IS_64BIT,!LONG_IS_64BIT \

# This clean filter does nothing, other than excercising the interface.
# We ensure that cleaning doesn't mangle large files on 64-bit Windows.
test_expect_success EXPENSIVE,SIZE_T_IS_64BIT,!LONG_IS_64BIT \
test_expect_success EXPENSIVE,SIZE_T_IS_64BIT \
'hash filtered files over 4GB correctly' '
{ test -f big || test-tool genzeros $((5*1024*1024*1024)) >big; } &&
test_oid large5GB >expect &&
Expand Down
Loading