]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/pull-tar.c
Merge pull request #907 from keszybz/sd-daemon-badf
[thirdparty/systemd.git] / src / import / pull-tar.c
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
25 #include "sd-daemon.h"
26 #include "utf8.h"
27 #include "strv.h"
28 #include "copy.h"
29 #include "btrfs-util.h"
30 #include "util.h"
31 #include "macro.h"
32 #include "mkdir.h"
33 #include "rm-rf.h"
34 #include "path-util.h"
35 #include "import-util.h"
36 #include "import-common.h"
37 #include "curl-util.h"
38 #include "pull-job.h"
39 #include "pull-common.h"
40 #include "pull-tar.h"
41 #include "process-util.h"
42
43 typedef enum TarProgress {
44 TAR_DOWNLOADING,
45 TAR_VERIFYING,
46 TAR_FINALIZING,
47 TAR_COPYING,
48 } TarProgress;
49
50 struct TarPull {
51 sd_event *event;
52 CurlGlue *glue;
53
54 char *image_root;
55
56 PullJob *tar_job;
57 PullJob *checksum_job;
58 PullJob *signature_job;
59
60 TarPullFinished on_finished;
61 void *userdata;
62
63 char *local;
64 bool force_local;
65 bool grow_machine_directory;
66
67 pid_t tar_pid;
68
69 char *temp_path;
70 char *final_path;
71
72 ImportVerify verify;
73 };
74
75 TarPull* tar_pull_unref(TarPull *i) {
76 if (!i)
77 return NULL;
78
79 if (i->tar_pid > 1) {
80 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
81 (void) wait_for_terminate(i->tar_pid, NULL);
82 }
83
84 pull_job_unref(i->tar_job);
85 pull_job_unref(i->checksum_job);
86 pull_job_unref(i->signature_job);
87
88 curl_glue_unref(i->glue);
89 sd_event_unref(i->event);
90
91 if (i->temp_path) {
92 (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
93 free(i->temp_path);
94 }
95
96 free(i->final_path);
97 free(i->image_root);
98 free(i->local);
99 free(i);
100
101 return NULL;
102 }
103
104 int tar_pull_new(
105 TarPull **ret,
106 sd_event *event,
107 const char *image_root,
108 TarPullFinished on_finished,
109 void *userdata) {
110
111 _cleanup_(tar_pull_unrefp) TarPull *i = NULL;
112 int r;
113
114 assert(ret);
115 assert(event);
116
117 i = new0(TarPull, 1);
118 if (!i)
119 return -ENOMEM;
120
121 i->on_finished = on_finished;
122 i->userdata = userdata;
123
124 i->image_root = strdup(image_root ?: "/var/lib/machines");
125 if (!i->image_root)
126 return -ENOMEM;
127
128 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
129
130 i->event = sd_event_ref(event);
131
132 r = curl_glue_new(&i->glue, i->event);
133 if (r < 0)
134 return r;
135
136 i->glue->on_finished = pull_job_curl_on_finished;
137 i->glue->userdata = i;
138
139 *ret = i;
140 i = NULL;
141
142 return 0;
143 }
144
145 static void tar_pull_report_progress(TarPull *i, TarProgress p) {
146 unsigned percent;
147
148 assert(i);
149
150 switch (p) {
151
152 case TAR_DOWNLOADING: {
153 unsigned remain = 85;
154
155 percent = 0;
156
157 if (i->checksum_job) {
158 percent += i->checksum_job->progress_percent * 5 / 100;
159 remain -= 5;
160 }
161
162 if (i->signature_job) {
163 percent += i->signature_job->progress_percent * 5 / 100;
164 remain -= 5;
165 }
166
167 if (i->tar_job)
168 percent += i->tar_job->progress_percent * remain / 100;
169 break;
170 }
171
172 case TAR_VERIFYING:
173 percent = 85;
174 break;
175
176 case TAR_FINALIZING:
177 percent = 90;
178 break;
179
180 case TAR_COPYING:
181 percent = 95;
182 break;
183
184 default:
185 assert_not_reached("Unknown progress state");
186 }
187
188 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
189 log_debug("Combined progress %u%%", percent);
190 }
191
192 static int tar_pull_make_local_copy(TarPull *i) {
193 int r;
194
195 assert(i);
196 assert(i->tar_job);
197
198 if (!i->local)
199 return 0;
200
201 if (!i->final_path) {
202 r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
203 if (r < 0)
204 return log_oom();
205 }
206
207 r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
208 if (r < 0)
209 return r;
210
211 return 0;
212 }
213
214 static bool tar_pull_is_done(TarPull *i) {
215 assert(i);
216 assert(i->tar_job);
217
218 if (i->tar_job->state != PULL_JOB_DONE)
219 return false;
220 if (i->checksum_job && i->checksum_job->state != PULL_JOB_DONE)
221 return false;
222 if (i->signature_job && i->signature_job->state != PULL_JOB_DONE)
223 return false;
224
225 return true;
226 }
227
228 static void tar_pull_job_on_finished(PullJob *j) {
229 TarPull *i;
230 int r;
231
232 assert(j);
233 assert(j->userdata);
234
235 i = j->userdata;
236 if (j->error != 0) {
237 if (j == i->checksum_job)
238 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
239 else if (j == i->signature_job)
240 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
241 else
242 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
243
244 r = j->error;
245 goto finish;
246 }
247
248 /* This is invoked if either the download completed
249 * successfully, or the download was skipped because we
250 * already have the etag. */
251
252 if (!tar_pull_is_done(i))
253 return;
254
255 j->disk_fd = safe_close(i->tar_job->disk_fd);
256
257 if (i->tar_pid > 0) {
258 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
259 i->tar_pid = 0;
260 if (r < 0)
261 goto finish;
262 }
263
264 if (!i->tar_job->etag_exists) {
265 /* This is a new download, verify it, and move it into place */
266
267 tar_pull_report_progress(i, TAR_VERIFYING);
268
269 r = pull_verify(i->tar_job, i->checksum_job, i->signature_job);
270 if (r < 0)
271 goto finish;
272
273 tar_pull_report_progress(i, TAR_FINALIZING);
274
275 r = import_make_read_only(i->temp_path);
276 if (r < 0)
277 goto finish;
278
279 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
280 if (r < 0) {
281 log_error_errno(r, "Failed to rename to final image name: %m");
282 goto finish;
283 }
284
285 free(i->temp_path);
286 i->temp_path = NULL;
287 }
288
289 tar_pull_report_progress(i, TAR_COPYING);
290
291 r = tar_pull_make_local_copy(i);
292 if (r < 0)
293 goto finish;
294
295 r = 0;
296
297 finish:
298 if (i->on_finished)
299 i->on_finished(i, r, i->userdata);
300 else
301 sd_event_exit(i->event, r);
302 }
303
304 static int tar_pull_job_on_open_disk(PullJob *j) {
305 TarPull *i;
306 int r;
307
308 assert(j);
309 assert(j->userdata);
310
311 i = j->userdata;
312 assert(i->tar_job == j);
313 assert(!i->final_path);
314 assert(!i->temp_path);
315 assert(i->tar_pid <= 0);
316
317 r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
318 if (r < 0)
319 return log_oom();
320
321 r = tempfn_random(i->final_path, NULL, &i->temp_path);
322 if (r < 0)
323 return log_oom();
324
325 mkdir_parents_label(i->temp_path, 0700);
326
327 r = btrfs_subvol_make(i->temp_path);
328 if (r == -ENOTTY) {
329 if (mkdir(i->temp_path, 0755) < 0)
330 return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
331 } else if (r < 0)
332 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
333
334 j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
335 if (j->disk_fd < 0)
336 return j->disk_fd;
337
338 return 0;
339 }
340
341 static void tar_pull_job_on_progress(PullJob *j) {
342 TarPull *i;
343
344 assert(j);
345 assert(j->userdata);
346
347 i = j->userdata;
348
349 tar_pull_report_progress(i, TAR_DOWNLOADING);
350 }
351
352 int tar_pull_start(TarPull *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
353 int r;
354
355 assert(i);
356
357 if (!http_url_is_valid(url))
358 return -EINVAL;
359
360 if (local && !machine_name_is_valid(local))
361 return -EINVAL;
362
363 if (i->tar_job)
364 return -EBUSY;
365
366 r = free_and_strdup(&i->local, local);
367 if (r < 0)
368 return r;
369 i->force_local = force_local;
370 i->verify = verify;
371
372 r = pull_job_new(&i->tar_job, url, i->glue, i);
373 if (r < 0)
374 return r;
375
376 i->tar_job->on_finished = tar_pull_job_on_finished;
377 i->tar_job->on_open_disk = tar_pull_job_on_open_disk;
378 i->tar_job->on_progress = tar_pull_job_on_progress;
379 i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
380 i->tar_job->grow_machine_directory = i->grow_machine_directory;
381
382 r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
383 if (r < 0)
384 return r;
385
386 r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
387 if (r < 0)
388 return r;
389
390 r = pull_job_begin(i->tar_job);
391 if (r < 0)
392 return r;
393
394 if (i->checksum_job) {
395 i->checksum_job->on_progress = tar_pull_job_on_progress;
396
397 r = pull_job_begin(i->checksum_job);
398 if (r < 0)
399 return r;
400 }
401
402 if (i->signature_job) {
403 i->signature_job->on_progress = tar_pull_job_on_progress;
404
405 r = pull_job_begin(i->signature_job);
406 if (r < 0)
407 return r;
408 }
409
410 return 0;
411 }