chiark / gitweb /
fsck: use _cleanup_close_pair_ where appropriate
[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                 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_x(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 }