169 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			C
		
	
	
| #include "cache.h"
 | |
| #include "bundle-uri.h"
 | |
| #include "bundle.h"
 | |
| #include "object-store.h"
 | |
| #include "refs.h"
 | |
| #include "run-command.h"
 | |
| 
 | |
| static int find_temp_filename(struct strbuf *name)
 | |
| {
 | |
| 	int fd;
 | |
| 	/*
 | |
| 	 * Find a temporary filename that is available. This is briefly
 | |
| 	 * racy, but unlikely to collide.
 | |
| 	 */
 | |
| 	fd = odb_mkstemp(name, "bundles/tmp_uri_XXXXXX");
 | |
| 	if (fd < 0) {
 | |
| 		warning(_("failed to create temporary file"));
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	close(fd);
 | |
| 	unlink(name->buf);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int download_https_uri_to_file(const char *file, const char *uri)
 | |
| {
 | |
| 	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);
 | |
| }
 | |
| 
 | |
| 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;
 | |
| 	struct strbuf filename = STRBUF_INIT;
 | |
| 
 | |
| 	if ((result = find_temp_filename(&filename)))
 | |
| 		goto cleanup;
 | |
| 
 | |
| 	if ((result = copy_uri_to_file(filename.buf, uri))) {
 | |
| 		warning(_("failed to download bundle from URI '%s'"), uri);
 | |
| 		goto cleanup;
 | |
| 	}
 | |
| 
 | |
| 	if ((result = !is_bundle(filename.buf, 0))) {
 | |
| 		warning(_("file at URI '%s' is not a bundle"), uri);
 | |
| 		goto cleanup;
 | |
| 	}
 | |
| 
 | |
| 	if ((result = unbundle_from_file(r, filename.buf))) {
 | |
| 		warning(_("failed to unbundle bundle from URI '%s'"), uri);
 | |
| 		goto cleanup;
 | |
| 	}
 | |
| 
 | |
| cleanup:
 | |
| 	unlink(filename.buf);
 | |
| 	strbuf_release(&filename);
 | |
| 	return result;
 | |
| }
 |