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