chiark / gitweb /
58cafdd7891d93c3710260ae66402ea12117ef65
[elogind.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                 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
285                 if (r < 0) {
286                         log_error_errno(r, "Failed to rename to final image name: %m");
287                         goto finish;
288                 }
289
290                 free(i->temp_path);
291                 i->temp_path = NULL;
292         }
293
294         tar_pull_report_progress(i, TAR_COPYING);
295
296         r = tar_pull_make_local_copy(i);
297         if (r < 0)
298                 goto finish;
299
300         r = 0;
301
302 finish:
303         if (i->on_finished)
304                 i->on_finished(i, r, i->userdata);
305         else
306                 sd_event_exit(i->event, r);
307 }
308
309 static int tar_pull_job_on_open_disk(PullJob *j) {
310         TarPull *i;
311         int r;
312
313         assert(j);
314         assert(j->userdata);
315
316         i = j->userdata;
317         assert(i->tar_job == j);
318         assert(!i->final_path);
319         assert(!i->temp_path);
320         assert(i->tar_pid <= 0);
321
322         r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
323         if (r < 0)
324                 return log_oom();
325
326         r = tempfn_random(i->final_path, &i->temp_path);
327         if (r < 0)
328                 return log_oom();
329
330         mkdir_parents_label(i->temp_path, 0700);
331
332         r = btrfs_subvol_make(i->temp_path);
333         if (r == -ENOTTY) {
334                 if (mkdir(i->temp_path, 0755) < 0)
335                         return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
336         } else if (r < 0)
337                 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
338
339         j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
340         if (j->disk_fd < 0)
341                 return j->disk_fd;
342
343         return 0;
344 }
345
346 static void tar_pull_job_on_progress(PullJob *j) {
347         TarPull *i;
348
349         assert(j);
350         assert(j->userdata);
351
352         i = j->userdata;
353
354         tar_pull_report_progress(i, TAR_DOWNLOADING);
355 }
356
357 int tar_pull_start(TarPull *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
358         int r;
359
360         assert(i);
361
362         if (!http_url_is_valid(url))
363                 return -EINVAL;
364
365         if (local && !machine_name_is_valid(local))
366                 return -EINVAL;
367
368         if (i->tar_job)
369                 return -EBUSY;
370
371         r = free_and_strdup(&i->local, local);
372         if (r < 0)
373                 return r;
374         i->force_local = force_local;
375         i->verify = verify;
376
377         r = pull_job_new(&i->tar_job, url, i->glue, i);
378         if (r < 0)
379                 return r;
380
381         i->tar_job->on_finished = tar_pull_job_on_finished;
382         i->tar_job->on_open_disk = tar_pull_job_on_open_disk;
383         i->tar_job->on_progress = tar_pull_job_on_progress;
384         i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
385         i->tar_job->grow_machine_directory = i->grow_machine_directory;
386
387         r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
388         if (r < 0)
389                 return r;
390
391         r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
392         if (r < 0)
393                 return r;
394
395         r = pull_job_begin(i->tar_job);
396         if (r < 0)
397                 return r;
398
399         if (i->checksum_job) {
400                 i->checksum_job->on_progress = tar_pull_job_on_progress;
401
402                 r = pull_job_begin(i->checksum_job);
403                 if (r < 0)
404                         return r;
405         }
406
407         if (i->signature_job) {
408                 i->signature_job->on_progress = tar_pull_job_on_progress;
409
410                 r = pull_job_begin(i->signature_job);
411                 if (r < 0)
412                         return r;
413         }
414
415         return 0;
416 }