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