2022-08-09 16:11:40 +03:00
|
|
|
#include "cache.h"
|
|
|
|
#include "bundle-uri.h"
|
|
|
|
#include "bundle.h"
|
|
|
|
#include "object-store.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "run-command.h"
|
|
|
|
|
2022-10-12 15:52:28 +03:00
|
|
|
static char *find_temp_filename(void)
|
2022-08-09 16:11:40 +03:00
|
|
|
{
|
|
|
|
int fd;
|
2022-10-12 15:52:28 +03:00
|
|
|
struct strbuf name = STRBUF_INIT;
|
2022-08-09 16:11:40 +03:00
|
|
|
/*
|
|
|
|
* Find a temporary filename that is available. This is briefly
|
|
|
|
* racy, but unlikely to collide.
|
|
|
|
*/
|
2022-10-12 15:52:28 +03:00
|
|
|
fd = odb_mkstemp(&name, "bundles/tmp_uri_XXXXXX");
|
2022-08-09 16:11:40 +03:00
|
|
|
if (fd < 0) {
|
|
|
|
warning(_("failed to create temporary file"));
|
2022-10-12 15:52:28 +03:00
|
|
|
return NULL;
|
2022-08-09 16:11:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
2022-10-12 15:52:28 +03:00
|
|
|
unlink(name.buf);
|
|
|
|
return strbuf_detach(&name, NULL);
|
2022-08-09 16:11:40 +03:00
|
|
|
}
|
|
|
|
|
2022-08-09 16:11:42 +03:00
|
|
|
static int download_https_uri_to_file(const char *file, const char *uri)
|
2022-08-09 16:11:40 +03:00
|
|
|
{
|
2022-08-09 16:11:42 +03:00
|
|
|
int result = 0;
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
FILE *child_in = NULL, *child_out = NULL;
|
|
|
|
struct strbuf line = STRBUF_INIT;
|
|
|
|
int found_get = 0;
|
|
|
|
|
|
|
|
strvec_pushl(&cp.args, "git-remote-https", uri, NULL);
|
|
|
|
cp.in = -1;
|
|
|
|
cp.out = -1;
|
|
|
|
|
|
|
|
if (start_command(&cp))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
child_in = fdopen(cp.in, "w");
|
|
|
|
if (!child_in) {
|
|
|
|
result = 1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
child_out = fdopen(cp.out, "r");
|
|
|
|
if (!child_out) {
|
|
|
|
result = 1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(child_in, "capabilities\n");
|
|
|
|
fflush(child_in);
|
|
|
|
|
|
|
|
while (!strbuf_getline(&line, child_out)) {
|
|
|
|
if (!line.len)
|
|
|
|
break;
|
|
|
|
if (!strcmp(line.buf, "get"))
|
|
|
|
found_get = 1;
|
|
|
|
}
|
|
|
|
strbuf_release(&line);
|
|
|
|
|
|
|
|
if (!found_get) {
|
|
|
|
result = error(_("insufficient capabilities"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(child_in, "get %s %s\n\n", uri, file);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (child_in)
|
|
|
|
fclose(child_in);
|
|
|
|
if (finish_command(&cp))
|
|
|
|
return 1;
|
|
|
|
if (child_out)
|
|
|
|
fclose(child_out);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int copy_uri_to_file(const char *filename, const char *uri)
|
|
|
|
{
|
|
|
|
const char *out;
|
|
|
|
|
|
|
|
if (starts_with(uri, "https:") ||
|
|
|
|
starts_with(uri, "http:"))
|
|
|
|
return download_https_uri_to_file(filename, uri);
|
|
|
|
|
|
|
|
if (skip_prefix(uri, "file://", &out))
|
|
|
|
uri = out;
|
|
|
|
|
|
|
|
/* Copy as a file */
|
|
|
|
return copy_file(filename, uri, 0);
|
2022-08-09 16:11:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int unbundle_from_file(struct repository *r, const char *file)
|
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
int bundle_fd;
|
|
|
|
struct bundle_header header = BUNDLE_HEADER_INIT;
|
|
|
|
struct string_list_item *refname;
|
|
|
|
struct strbuf bundle_ref = STRBUF_INIT;
|
|
|
|
size_t bundle_prefix_len;
|
|
|
|
|
|
|
|
if ((bundle_fd = read_bundle_header(file, &header)) < 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if ((result = unbundle(r, &header, bundle_fd, NULL)))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert all refs/heads/ from the bundle into refs/bundles/
|
|
|
|
* in the local repository.
|
|
|
|
*/
|
|
|
|
strbuf_addstr(&bundle_ref, "refs/bundles/");
|
|
|
|
bundle_prefix_len = bundle_ref.len;
|
|
|
|
|
|
|
|
for_each_string_list_item(refname, &header.references) {
|
|
|
|
struct object_id *oid = refname->util;
|
|
|
|
struct object_id old_oid;
|
|
|
|
const char *branch_name;
|
|
|
|
int has_old;
|
|
|
|
|
|
|
|
if (!skip_prefix(refname->string, "refs/heads/", &branch_name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
strbuf_setlen(&bundle_ref, bundle_prefix_len);
|
|
|
|
strbuf_addstr(&bundle_ref, branch_name);
|
|
|
|
|
|
|
|
has_old = !read_ref(bundle_ref.buf, &old_oid);
|
|
|
|
update_ref("fetched bundle", bundle_ref.buf, oid,
|
|
|
|
has_old ? &old_oid : NULL,
|
|
|
|
REF_SKIP_OID_VERIFICATION,
|
|
|
|
UPDATE_REFS_MSG_ON_ERR);
|
|
|
|
}
|
|
|
|
|
|
|
|
bundle_header_release(&header);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fetch_bundle_uri(struct repository *r, const char *uri)
|
|
|
|
{
|
|
|
|
int result = 0;
|
2022-10-12 15:52:28 +03:00
|
|
|
char *filename;
|
2022-08-09 16:11:40 +03:00
|
|
|
|
2022-10-12 15:52:28 +03:00
|
|
|
if (!(filename = find_temp_filename())) {
|
|
|
|
result = -1;
|
2022-08-09 16:11:40 +03:00
|
|
|
goto cleanup;
|
2022-10-12 15:52:28 +03:00
|
|
|
}
|
2022-08-09 16:11:40 +03:00
|
|
|
|
2022-10-12 15:52:28 +03:00
|
|
|
if ((result = copy_uri_to_file(filename, uri))) {
|
2022-08-09 16:11:40 +03:00
|
|
|
warning(_("failed to download bundle from URI '%s'"), uri);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2022-10-12 15:52:28 +03:00
|
|
|
if ((result = !is_bundle(filename, 0))) {
|
2022-08-09 16:11:40 +03:00
|
|
|
warning(_("file at URI '%s' is not a bundle"), uri);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2022-10-12 15:52:28 +03:00
|
|
|
if ((result = unbundle_from_file(r, filename))) {
|
2022-08-09 16:11:40 +03:00
|
|
|
warning(_("failed to unbundle bundle from URI '%s'"), uri);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
2022-10-12 15:52:28 +03:00
|
|
|
if (filename)
|
|
|
|
unlink(filename);
|
|
|
|
free(filename);
|
2022-08-09 16:11:40 +03:00
|
|
|
return result;
|
|
|
|
}
|