]>
Commit | Line | Data |
---|---|---|
53a50892 DS |
1 | #include "cache.h" |
2 | #include "bundle-uri.h" | |
3 | #include "bundle.h" | |
4 | #include "object-store.h" | |
5 | #include "refs.h" | |
6 | #include "run-command.h" | |
7 | ||
23b6d00b | 8 | static char *find_temp_filename(void) |
53a50892 DS |
9 | { |
10 | int fd; | |
23b6d00b | 11 | struct strbuf name = STRBUF_INIT; |
53a50892 DS |
12 | /* |
13 | * Find a temporary filename that is available. This is briefly | |
14 | * racy, but unlikely to collide. | |
15 | */ | |
23b6d00b | 16 | fd = odb_mkstemp(&name, "bundles/tmp_uri_XXXXXX"); |
53a50892 DS |
17 | if (fd < 0) { |
18 | warning(_("failed to create temporary file")); | |
23b6d00b | 19 | return NULL; |
53a50892 DS |
20 | } |
21 | ||
22 | close(fd); | |
23b6d00b DS |
23 | unlink(name.buf); |
24 | return strbuf_detach(&name, NULL); | |
53a50892 DS |
25 | } |
26 | ||
59c1752a | 27 | static int download_https_uri_to_file(const char *file, const char *uri) |
53a50892 | 28 | { |
59c1752a DS |
29 | int result = 0; |
30 | struct child_process cp = CHILD_PROCESS_INIT; | |
31 | FILE *child_in = NULL, *child_out = NULL; | |
32 | struct strbuf line = STRBUF_INIT; | |
33 | int found_get = 0; | |
34 | ||
35 | strvec_pushl(&cp.args, "git-remote-https", uri, NULL); | |
36 | cp.in = -1; | |
37 | cp.out = -1; | |
38 | ||
39 | if (start_command(&cp)) | |
40 | return 1; | |
41 | ||
42 | child_in = fdopen(cp.in, "w"); | |
43 | if (!child_in) { | |
44 | result = 1; | |
45 | goto cleanup; | |
46 | } | |
47 | ||
48 | child_out = fdopen(cp.out, "r"); | |
49 | if (!child_out) { | |
50 | result = 1; | |
51 | goto cleanup; | |
52 | } | |
53 | ||
54 | fprintf(child_in, "capabilities\n"); | |
55 | fflush(child_in); | |
56 | ||
57 | while (!strbuf_getline(&line, child_out)) { | |
58 | if (!line.len) | |
59 | break; | |
60 | if (!strcmp(line.buf, "get")) | |
61 | found_get = 1; | |
62 | } | |
63 | strbuf_release(&line); | |
64 | ||
65 | if (!found_get) { | |
66 | result = error(_("insufficient capabilities")); | |
67 | goto cleanup; | |
68 | } | |
69 | ||
70 | fprintf(child_in, "get %s %s\n\n", uri, file); | |
71 | ||
72 | cleanup: | |
73 | if (child_in) | |
74 | fclose(child_in); | |
75 | if (finish_command(&cp)) | |
76 | return 1; | |
77 | if (child_out) | |
78 | fclose(child_out); | |
79 | return result; | |
80 | } | |
81 | ||
82 | static int copy_uri_to_file(const char *filename, const char *uri) | |
83 | { | |
84 | const char *out; | |
85 | ||
86 | if (starts_with(uri, "https:") || | |
87 | starts_with(uri, "http:")) | |
88 | return download_https_uri_to_file(filename, uri); | |
89 | ||
90 | if (skip_prefix(uri, "file://", &out)) | |
91 | uri = out; | |
92 | ||
93 | /* Copy as a file */ | |
94 | return copy_file(filename, uri, 0); | |
53a50892 DS |
95 | } |
96 | ||
97 | static int unbundle_from_file(struct repository *r, const char *file) | |
98 | { | |
99 | int result = 0; | |
100 | int bundle_fd; | |
101 | struct bundle_header header = BUNDLE_HEADER_INIT; | |
102 | struct string_list_item *refname; | |
103 | struct strbuf bundle_ref = STRBUF_INIT; | |
104 | size_t bundle_prefix_len; | |
105 | ||
106 | if ((bundle_fd = read_bundle_header(file, &header)) < 0) | |
107 | return 1; | |
108 | ||
109 | if ((result = unbundle(r, &header, bundle_fd, NULL))) | |
110 | return 1; | |
111 | ||
112 | /* | |
113 | * Convert all refs/heads/ from the bundle into refs/bundles/ | |
114 | * in the local repository. | |
115 | */ | |
116 | strbuf_addstr(&bundle_ref, "refs/bundles/"); | |
117 | bundle_prefix_len = bundle_ref.len; | |
118 | ||
119 | for_each_string_list_item(refname, &header.references) { | |
120 | struct object_id *oid = refname->util; | |
121 | struct object_id old_oid; | |
122 | const char *branch_name; | |
123 | int has_old; | |
124 | ||
125 | if (!skip_prefix(refname->string, "refs/heads/", &branch_name)) | |
126 | continue; | |
127 | ||
128 | strbuf_setlen(&bundle_ref, bundle_prefix_len); | |
129 | strbuf_addstr(&bundle_ref, branch_name); | |
130 | ||
131 | has_old = !read_ref(bundle_ref.buf, &old_oid); | |
132 | update_ref("fetched bundle", bundle_ref.buf, oid, | |
133 | has_old ? &old_oid : NULL, | |
134 | REF_SKIP_OID_VERIFICATION, | |
135 | UPDATE_REFS_MSG_ON_ERR); | |
136 | } | |
137 | ||
138 | bundle_header_release(&header); | |
139 | return result; | |
140 | } | |
141 | ||
142 | int fetch_bundle_uri(struct repository *r, const char *uri) | |
143 | { | |
144 | int result = 0; | |
23b6d00b | 145 | char *filename; |
53a50892 | 146 | |
23b6d00b DS |
147 | if (!(filename = find_temp_filename())) { |
148 | result = -1; | |
53a50892 | 149 | goto cleanup; |
23b6d00b | 150 | } |
53a50892 | 151 | |
23b6d00b | 152 | if ((result = copy_uri_to_file(filename, uri))) { |
53a50892 DS |
153 | warning(_("failed to download bundle from URI '%s'"), uri); |
154 | goto cleanup; | |
155 | } | |
156 | ||
23b6d00b | 157 | if ((result = !is_bundle(filename, 0))) { |
53a50892 DS |
158 | warning(_("file at URI '%s' is not a bundle"), uri); |
159 | goto cleanup; | |
160 | } | |
161 | ||
23b6d00b | 162 | if ((result = unbundle_from_file(r, filename))) { |
53a50892 DS |
163 | warning(_("failed to unbundle bundle from URI '%s'"), uri); |
164 | goto cleanup; | |
165 | } | |
166 | ||
167 | cleanup: | |
23b6d00b DS |
168 | if (filename) |
169 | unlink(filename); | |
170 | free(filename); | |
53a50892 DS |
171 | return result; |
172 | } |