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