]>
Commit | Line | Data |
---|---|---|
56ebfaf1 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2015 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #include <sys/prctl.h> | |
23 | #include <curl/curl.h> | |
24 | ||
56ebfaf1 LP |
25 | #include "utf8.h" |
26 | #include "strv.h" | |
27 | #include "copy.h" | |
28 | #include "btrfs-util.h" | |
29 | #include "util.h" | |
30 | #include "macro.h" | |
31 | #include "mkdir.h" | |
3d7415f4 | 32 | #include "import-util.h" |
56ebfaf1 LP |
33 | #include "curl-util.h" |
34 | #include "import-job.h" | |
3d7415f4 | 35 | #include "import-common.h" |
56ebfaf1 LP |
36 | #include "import-tar.h" |
37 | ||
38 | struct TarImport { | |
39 | sd_event *event; | |
40 | CurlGlue *glue; | |
41 | ||
42 | char *image_root; | |
43 | ||
44 | ImportJob *tar_job; | |
0100b6e1 LP |
45 | ImportJob *checksum_job; |
46 | ImportJob *signature_job; | |
56ebfaf1 LP |
47 | |
48 | TarImportFinished on_finished; | |
49 | void *userdata; | |
50 | ||
56ebfaf1 LP |
51 | char *local; |
52 | bool force_local; | |
53 | ||
54 | pid_t tar_pid; | |
55 | ||
56 | char *temp_path; | |
57 | char *final_path; | |
0100b6e1 LP |
58 | |
59 | ImportVerify verify; | |
56ebfaf1 LP |
60 | }; |
61 | ||
62 | TarImport* tar_import_unref(TarImport *i) { | |
63 | if (!i) | |
64 | return NULL; | |
65 | ||
0100b6e1 | 66 | if (i->tar_pid > 1) { |
8b71fce8 | 67 | (void) kill_and_sigcont(i->tar_pid, SIGKILL); |
0100b6e1 | 68 | (void) wait_for_terminate(i->tar_pid, NULL); |
56ebfaf1 LP |
69 | } |
70 | ||
71 | import_job_unref(i->tar_job); | |
0100b6e1 LP |
72 | import_job_unref(i->checksum_job); |
73 | import_job_unref(i->signature_job); | |
56ebfaf1 LP |
74 | |
75 | curl_glue_unref(i->glue); | |
76 | sd_event_unref(i->event); | |
77 | ||
78 | if (i->temp_path) { | |
79 | (void) btrfs_subvol_remove(i->temp_path); | |
80 | (void) rm_rf_dangerous(i->temp_path, false, true, false); | |
0d6e763b | 81 | free(i->temp_path); |
56ebfaf1 LP |
82 | } |
83 | ||
84 | free(i->final_path); | |
85 | free(i->image_root); | |
86 | free(i->local); | |
56ebfaf1 LP |
87 | free(i); |
88 | ||
89 | return NULL; | |
90 | } | |
91 | ||
8b71fce8 LP |
92 | int tar_import_new( |
93 | TarImport **ret, | |
94 | sd_event *event, | |
95 | const char *image_root, | |
96 | TarImportFinished on_finished, | |
97 | void *userdata) { | |
98 | ||
56ebfaf1 LP |
99 | _cleanup_(tar_import_unrefp) TarImport *i = NULL; |
100 | int r; | |
101 | ||
102 | assert(ret); | |
103 | assert(event); | |
104 | ||
105 | i = new0(TarImport, 1); | |
106 | if (!i) | |
107 | return -ENOMEM; | |
108 | ||
109 | i->on_finished = on_finished; | |
110 | i->userdata = userdata; | |
111 | ||
112 | i->image_root = strdup(image_root ?: "/var/lib/machines"); | |
113 | if (!i->image_root) | |
114 | return -ENOMEM; | |
115 | ||
116 | if (event) | |
117 | i->event = sd_event_ref(event); | |
118 | else { | |
119 | r = sd_event_default(&i->event); | |
120 | if (r < 0) | |
121 | return r; | |
122 | } | |
123 | ||
124 | r = curl_glue_new(&i->glue, i->event); | |
125 | if (r < 0) | |
126 | return r; | |
127 | ||
128 | i->glue->on_finished = import_job_curl_on_finished; | |
129 | i->glue->userdata = i; | |
130 | ||
131 | *ret = i; | |
132 | i = NULL; | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
0d6e763b LP |
137 | static int tar_import_make_local_copy(TarImport *i) { |
138 | int r; | |
139 | ||
140 | assert(i); | |
141 | assert(i->tar_job); | |
142 | ||
143 | if (!i->local) | |
144 | return 0; | |
145 | ||
146 | if (!i->final_path) { | |
147 | r = import_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path); | |
148 | if (r < 0) | |
149 | return log_oom(); | |
0d6e763b LP |
150 | } |
151 | ||
0100b6e1 LP |
152 | r = import_make_local_copy(i->final_path, i->image_root, i->local, i->force_local); |
153 | if (r < 0) | |
154 | return r; | |
155 | ||
0d6e763b LP |
156 | return 0; |
157 | } | |
158 | ||
8b71fce8 LP |
159 | static bool tar_import_is_done(TarImport *i) { |
160 | assert(i); | |
161 | assert(i->tar_job); | |
162 | ||
163 | if (i->tar_job->state != IMPORT_JOB_DONE) | |
164 | return false; | |
165 | if (i->checksum_job && i->checksum_job->state != IMPORT_JOB_DONE) | |
166 | return false; | |
167 | if (i->signature_job && i->signature_job->state != IMPORT_JOB_DONE) | |
168 | return false; | |
169 | ||
170 | return true; | |
171 | } | |
172 | ||
56ebfaf1 LP |
173 | static void tar_import_job_on_finished(ImportJob *j) { |
174 | TarImport *i; | |
175 | int r; | |
176 | ||
177 | assert(j); | |
178 | assert(j->userdata); | |
179 | ||
180 | i = j->userdata; | |
56ebfaf1 | 181 | if (j->error != 0) { |
0100b6e1 LP |
182 | if (j == i->checksum_job) |
183 | log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); | |
184 | else if (j == i->signature_job) | |
185 | log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); | |
186 | else | |
187 | log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); | |
188 | ||
56ebfaf1 LP |
189 | r = j->error; |
190 | goto finish; | |
191 | } | |
192 | ||
193 | /* This is invoked if either the download completed | |
194 | * successfully, or the download was skipped because we | |
195 | * already have the etag. */ | |
196 | ||
8b71fce8 | 197 | if (!tar_import_is_done(i)) |
0100b6e1 LP |
198 | return; |
199 | ||
200 | j->disk_fd = safe_close(i->tar_job->disk_fd); | |
56ebfaf1 LP |
201 | |
202 | if (i->tar_pid > 0) { | |
203 | r = wait_for_terminate_and_warn("tar", i->tar_pid, true); | |
204 | i->tar_pid = 0; | |
205 | if (r < 0) | |
206 | goto finish; | |
207 | } | |
208 | ||
0100b6e1 LP |
209 | if (!i->tar_job->etag_exists) { |
210 | /* This is a new download, verify it, and move it into place */ | |
211 | ||
212 | r = import_verify(i->tar_job, i->checksum_job, i->signature_job); | |
213 | if (r < 0) | |
214 | goto finish; | |
215 | ||
56ebfaf1 LP |
216 | r = import_make_read_only(i->temp_path); |
217 | if (r < 0) | |
218 | goto finish; | |
219 | ||
220 | if (rename(i->temp_path, i->final_path) < 0) { | |
221 | r = log_error_errno(errno, "Failed to rename to final image name: %m"); | |
222 | goto finish; | |
223 | } | |
56ebfaf1 | 224 | |
0d6e763b LP |
225 | free(i->temp_path); |
226 | i->temp_path = NULL; | |
56ebfaf1 LP |
227 | } |
228 | ||
0d6e763b LP |
229 | r = tar_import_make_local_copy(i); |
230 | if (r < 0) | |
231 | goto finish; | |
232 | ||
56ebfaf1 LP |
233 | r = 0; |
234 | ||
235 | finish: | |
56ebfaf1 LP |
236 | if (i->on_finished) |
237 | i->on_finished(i, r, i->userdata); | |
238 | else | |
239 | sd_event_exit(i->event, r); | |
240 | } | |
241 | ||
242 | static int tar_import_job_on_open_disk(ImportJob *j) { | |
243 | _cleanup_close_pair_ int pipefd[2] = { -1 , -1 }; | |
244 | TarImport *i; | |
245 | int r; | |
246 | ||
247 | assert(j); | |
248 | assert(j->userdata); | |
249 | ||
250 | i = j->userdata; | |
8b71fce8 LP |
251 | assert(i->tar_job == j); |
252 | assert(!i->final_path); | |
253 | assert(!i->temp_path); | |
254 | assert(i->tar_pid <= 0); | |
56ebfaf1 LP |
255 | |
256 | r = import_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path); | |
257 | if (r < 0) | |
258 | return log_oom(); | |
259 | ||
260 | r = tempfn_random(i->final_path, &i->temp_path); | |
261 | if (r < 0) | |
262 | return log_oom(); | |
263 | ||
264 | mkdir_parents_label(i->temp_path, 0700); | |
265 | ||
266 | r = btrfs_subvol_make(i->temp_path); | |
267 | if (r == -ENOTTY) { | |
268 | if (mkdir(i->temp_path, 0755) < 0) | |
269 | return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); | |
270 | } else if (r < 0) | |
271 | return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path); | |
272 | ||
273 | if (pipe2(pipefd, O_CLOEXEC) < 0) | |
274 | return log_error_errno(errno, "Failed to create pipe for tar: %m"); | |
275 | ||
276 | i->tar_pid = fork(); | |
277 | if (i->tar_pid < 0) | |
278 | return log_error_errno(errno, "Failed to fork off tar: %m"); | |
279 | if (i->tar_pid == 0) { | |
280 | int null_fd; | |
281 | ||
0100b6e1 LP |
282 | /* Child */ |
283 | ||
56ebfaf1 LP |
284 | reset_all_signal_handlers(); |
285 | reset_signal_mask(); | |
286 | assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); | |
287 | ||
288 | pipefd[1] = safe_close(pipefd[1]); | |
289 | ||
290 | if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) { | |
291 | log_error_errno(errno, "Failed to dup2() fd: %m"); | |
292 | _exit(EXIT_FAILURE); | |
293 | } | |
294 | ||
295 | if (pipefd[0] != STDIN_FILENO) | |
f4c135bf | 296 | pipefd[0] = safe_close(pipefd[0]); |
56ebfaf1 LP |
297 | |
298 | null_fd = open("/dev/null", O_WRONLY|O_NOCTTY); | |
299 | if (null_fd < 0) { | |
300 | log_error_errno(errno, "Failed to open /dev/null: %m"); | |
301 | _exit(EXIT_FAILURE); | |
302 | } | |
303 | ||
304 | if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) { | |
305 | log_error_errno(errno, "Failed to dup2() fd: %m"); | |
306 | _exit(EXIT_FAILURE); | |
307 | } | |
308 | ||
309 | if (null_fd != STDOUT_FILENO) | |
f4c135bf LP |
310 | null_fd = safe_close(null_fd); |
311 | ||
312 | fd_cloexec(STDIN_FILENO, false); | |
313 | fd_cloexec(STDOUT_FILENO, false); | |
314 | fd_cloexec(STDERR_FILENO, false); | |
56ebfaf1 LP |
315 | |
316 | execlp("tar", "tar", "--numeric-owner", "-C", i->temp_path, "-px", NULL); | |
5a3b1abd | 317 | log_error_errno(errno, "Failed to execute tar: %m"); |
56ebfaf1 LP |
318 | _exit(EXIT_FAILURE); |
319 | } | |
320 | ||
321 | pipefd[0] = safe_close(pipefd[0]); | |
322 | ||
323 | j->disk_fd = pipefd[1]; | |
324 | pipefd[1] = -1; | |
325 | ||
326 | return 0; | |
327 | } | |
328 | ||
0100b6e1 | 329 | int tar_import_pull(TarImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) { |
56ebfaf1 LP |
330 | int r; |
331 | ||
332 | assert(i); | |
333 | ||
56ebfaf1 LP |
334 | if (!http_url_is_valid(url)) |
335 | return -EINVAL; | |
336 | ||
337 | if (local && !machine_name_is_valid(local)) | |
338 | return -EINVAL; | |
339 | ||
8b71fce8 LP |
340 | if (i->tar_job) |
341 | return -EBUSY; | |
342 | ||
56ebfaf1 LP |
343 | r = free_and_strdup(&i->local, local); |
344 | if (r < 0) | |
345 | return r; | |
56ebfaf1 | 346 | i->force_local = force_local; |
0100b6e1 | 347 | i->verify = verify; |
56ebfaf1 LP |
348 | |
349 | r = import_job_new(&i->tar_job, url, i->glue, i); | |
350 | if (r < 0) | |
351 | return r; | |
352 | ||
353 | i->tar_job->on_finished = tar_import_job_on_finished; | |
354 | i->tar_job->on_open_disk = tar_import_job_on_open_disk; | |
0100b6e1 | 355 | i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO; |
56ebfaf1 LP |
356 | |
357 | r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags); | |
358 | if (r < 0) | |
359 | return r; | |
360 | ||
0100b6e1 LP |
361 | r = import_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_import_job_on_finished, i); |
362 | if (r < 0) | |
363 | return r; | |
364 | ||
365 | r = import_job_begin(i->tar_job); | |
366 | if (r < 0) | |
367 | return r; | |
368 | ||
369 | if (i->checksum_job) { | |
370 | r = import_job_begin(i->checksum_job); | |
371 | if (r < 0) | |
372 | return r; | |
373 | } | |
374 | ||
375 | if (i->signature_job) { | |
376 | r = import_job_begin(i->signature_job); | |
377 | if (r < 0) | |
378 | return r; | |
379 | } | |
380 | ||
381 | return 0; | |
56ebfaf1 | 382 | } |