]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/pull-tar.c
importd: add new bus calls for importing local tar and raw images
[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 "path-util.h"
34 #include "import-util.h"
35 #include "import-common.h"
36 #include "curl-util.h"
37 #include "pull-job.h"
38 #include "pull-common.h"
39 #include "pull-tar.h"
40
41 typedef enum TarProgress {
42 TAR_DOWNLOADING,
43 TAR_VERIFYING,
44 TAR_FINALIZING,
45 TAR_COPYING,
46 } TarProgress;
47
48 struct TarPull {
49 sd_event *event;
50 CurlGlue *glue;
51
52 char *image_root;
53
54 PullJob *tar_job;
55 PullJob *checksum_job;
56 PullJob *signature_job;
57
58 TarPullFinished on_finished;
59 void *userdata;
60
61 char *local;
62 bool force_local;
63 bool grow_machine_directory;
64
65 pid_t tar_pid;
66
67 char *temp_path;
68 char *final_path;
69
70 ImportVerify verify;
71 };
72
73 TarPull* tar_pull_unref(TarPull *i) {
74 if (!i)
75 return NULL;
76
77 if (i->tar_pid > 1) {
78 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
79 (void) wait_for_terminate(i->tar_pid, NULL);
80 }
81
82 pull_job_unref(i->tar_job);
83 pull_job_unref(i->checksum_job);
84 pull_job_unref(i->signature_job);
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);
92 free(i->temp_path);
93 }
94
95 free(i->final_path);
96 free(i->image_root);
97 free(i->local);
98 free(i);
99
100 return NULL;
101 }
102
103 int tar_pull_new(
104 TarPull **ret,
105 sd_event *event,
106 const char *image_root,
107 TarPullFinished on_finished,
108 void *userdata) {
109
110 _cleanup_(tar_pull_unrefp) TarPull *i = NULL;
111 int r;
112
113 assert(ret);
114 assert(event);
115
116 i = new0(TarPull, 1);
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
127 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
128
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
141 i->glue->on_finished = pull_job_curl_on_finished;
142 i->glue->userdata = i;
143
144 *ret = i;
145 i = NULL;
146
147 return 0;
148 }
149
150 static void tar_pull_report_progress(TarPull *i, TarProgress p) {
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
197 static int tar_pull_make_local_copy(TarPull *i) {
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) {
207 r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
208 if (r < 0)
209 return log_oom();
210 }
211
212 r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
213 if (r < 0)
214 return r;
215
216 return 0;
217 }
218
219 static bool tar_pull_is_done(TarPull *i) {
220 assert(i);
221 assert(i->tar_job);
222
223 if (i->tar_job->state != PULL_JOB_DONE)
224 return false;
225 if (i->checksum_job && i->checksum_job->state != PULL_JOB_DONE)
226 return false;
227 if (i->signature_job && i->signature_job->state != PULL_JOB_DONE)
228 return false;
229
230 return true;
231 }
232
233 static void tar_pull_job_on_finished(PullJob *j) {
234 TarPull *i;
235 int r;
236
237 assert(j);
238 assert(j->userdata);
239
240 i = j->userdata;
241 if (j->error != 0) {
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
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
257 if (!tar_pull_is_done(i))
258 return;
259
260 j->disk_fd = safe_close(i->tar_job->disk_fd);
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
269 if (!i->tar_job->etag_exists) {
270 /* This is a new download, verify it, and move it into place */
271
272 tar_pull_report_progress(i, TAR_VERIFYING);
273
274 r = pull_verify(i->tar_job, i->checksum_job, i->signature_job);
275 if (r < 0)
276 goto finish;
277
278 tar_pull_report_progress(i, TAR_FINALIZING);
279
280 r = import_make_read_only(i->temp_path);
281 if (r < 0)
282 goto finish;
283
284 if (renameat2(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path, RENAME_NOREPLACE) < 0) {
285 r = log_error_errno(errno, "Failed to rename to final image name: %m");
286 goto finish;
287 }
288
289 free(i->temp_path);
290 i->temp_path = NULL;
291 }
292
293 tar_pull_report_progress(i, TAR_COPYING);
294
295 r = tar_pull_make_local_copy(i);
296 if (r < 0)
297 goto finish;
298
299 r = 0;
300
301 finish:
302 if (i->on_finished)
303 i->on_finished(i, r, i->userdata);
304 else
305 sd_event_exit(i->event, r);
306 }
307
308 static int tar_pull_job_on_open_disk(PullJob *j) {
309 TarPull *i;
310 int r;
311
312 assert(j);
313 assert(j->userdata);
314
315 i = j->userdata;
316 assert(i->tar_job == j);
317 assert(!i->final_path);
318 assert(!i->temp_path);
319 assert(i->tar_pid <= 0);
320
321 r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
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
338 j->disk_fd = import_fork_tar(i->temp_path, &i->tar_pid);
339 if (j->disk_fd < 0)
340 return j->disk_fd;
341
342 return 0;
343 }
344
345 static void tar_pull_job_on_progress(PullJob *j) {
346 TarPull *i;
347
348 assert(j);
349 assert(j->userdata);
350
351 i = j->userdata;
352
353 tar_pull_report_progress(i, TAR_DOWNLOADING);
354 }
355
356 int tar_pull_start(TarPull *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
357 int r;
358
359 assert(i);
360
361 if (!http_url_is_valid(url))
362 return -EINVAL;
363
364 if (local && !machine_name_is_valid(local))
365 return -EINVAL;
366
367 if (i->tar_job)
368 return -EBUSY;
369
370 r = free_and_strdup(&i->local, local);
371 if (r < 0)
372 return r;
373 i->force_local = force_local;
374 i->verify = verify;
375
376 r = pull_job_new(&i->tar_job, url, i->glue, i);
377 if (r < 0)
378 return r;
379
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;
383 i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
384 i->tar_job->grow_machine_directory = i->grow_machine_directory;
385
386 r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
387 if (r < 0)
388 return r;
389
390 r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
391 if (r < 0)
392 return r;
393
394 r = pull_job_begin(i->tar_job);
395 if (r < 0)
396 return r;
397
398 if (i->checksum_job) {
399 i->checksum_job->on_progress = tar_pull_job_on_progress;
400
401 r = pull_job_begin(i->checksum_job);
402 if (r < 0)
403 return r;
404 }
405
406 if (i->signature_job) {
407 i->signature_job->on_progress = tar_pull_job_on_progress;
408
409 r = pull_job_begin(i->signature_job);
410 if (r < 0)
411 return r;
412 }
413
414 return 0;
415 }