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