chiark / gitweb /
80ae83971efb93dd03164584bda76dff2c4d1797
[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 "utf8.h"
26 #include "strv.h"
27 #include "copy.h"
28 #include "btrfs-util.h"
29 #include "util.h"
30 #include "macro.h"
31 #include "mkdir.h"
32 #include "import-util.h"
33 #include "curl-util.h"
34 #include "import-job.h"
35 #include "import-common.h"
36 #include "import-tar.h"
37
38 struct TarImport {
39         sd_event *event;
40         CurlGlue *glue;
41
42         char *image_root;
43
44         ImportJob *tar_job;
45         ImportJob *checksum_job;
46         ImportJob *signature_job;
47
48         TarImportFinished on_finished;
49         void *userdata;
50
51         char *local;
52         bool force_local;
53
54         pid_t tar_pid;
55
56         char *temp_path;
57         char *final_path;
58
59         ImportVerify verify;
60 };
61
62 TarImport* tar_import_unref(TarImport *i) {
63         if (!i)
64                 return NULL;
65
66         if (i->tar_pid > 1) {
67                 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
68                 (void) wait_for_terminate(i->tar_pid, NULL);
69         }
70
71         import_job_unref(i->tar_job);
72         import_job_unref(i->checksum_job);
73         import_job_unref(i->signature_job);
74
75         curl_glue_unref(i->glue);
76         sd_event_unref(i->event);
77
78         if (i->temp_path) {
79                 (void) btrfs_subvol_remove(i->temp_path);
80                 (void) rm_rf_dangerous(i->temp_path, false, true, false);
81                 free(i->temp_path);
82         }
83
84         free(i->final_path);
85         free(i->image_root);
86         free(i->local);
87         free(i);
88
89         return NULL;
90 }
91
92 int tar_import_new(
93                 TarImport **ret,
94                 sd_event *event,
95                 const char *image_root,
96                 TarImportFinished on_finished,
97                 void *userdata) {
98
99         _cleanup_(tar_import_unrefp) TarImport *i = NULL;
100         int r;
101
102         assert(ret);
103         assert(event);
104
105         i = new0(TarImport, 1);
106         if (!i)
107                 return -ENOMEM;
108
109         i->on_finished = on_finished;
110         i->userdata = userdata;
111
112         i->image_root = strdup(image_root ?: "/var/lib/machines");
113         if (!i->image_root)
114                 return -ENOMEM;
115
116         if (event)
117                 i->event = sd_event_ref(event);
118         else {
119                 r = sd_event_default(&i->event);
120                 if (r < 0)
121                         return r;
122         }
123
124         r = curl_glue_new(&i->glue, i->event);
125         if (r < 0)
126                 return r;
127
128         i->glue->on_finished = import_job_curl_on_finished;
129         i->glue->userdata = i;
130
131         *ret = i;
132         i = NULL;
133
134         return 0;
135 }
136
137 static int tar_import_make_local_copy(TarImport *i) {
138         int r;
139
140         assert(i);
141         assert(i->tar_job);
142
143         if (!i->local)
144                 return 0;
145
146         if (!i->final_path) {
147                 r = import_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
148                 if (r < 0)
149                         return log_oom();
150         }
151
152         r = import_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
153         if (r < 0)
154                 return r;
155
156         return 0;
157 }
158
159 static bool tar_import_is_done(TarImport *i) {
160         assert(i);
161         assert(i->tar_job);
162
163         if (i->tar_job->state != IMPORT_JOB_DONE)
164                 return false;
165         if (i->checksum_job && i->checksum_job->state != IMPORT_JOB_DONE)
166                 return false;
167         if (i->signature_job && i->signature_job->state != IMPORT_JOB_DONE)
168                 return false;
169
170         return true;
171 }
172
173 static void tar_import_job_on_finished(ImportJob *j) {
174         TarImport *i;
175         int r;
176
177         assert(j);
178         assert(j->userdata);
179
180         i = j->userdata;
181         if (j->error != 0) {
182                 if (j == i->checksum_job)
183                         log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
184                 else if (j == i->signature_job)
185                         log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
186                 else
187                         log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
188
189                 r = j->error;
190                 goto finish;
191         }
192
193         /* This is invoked if either the download completed
194          * successfully, or the download was skipped because we
195          * already have the etag. */
196
197         if (!tar_import_is_done(i))
198                 return;
199
200         j->disk_fd = safe_close(i->tar_job->disk_fd);
201
202         if (i->tar_pid > 0) {
203                 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
204                 i->tar_pid = 0;
205                 if (r < 0)
206                         goto finish;
207         }
208
209         if (!i->tar_job->etag_exists) {
210                 /* This is a new download, verify it, and move it into place */
211
212                 r = import_verify(i->tar_job, i->checksum_job, i->signature_job);
213                 if (r < 0)
214                         goto finish;
215
216                 r = import_make_read_only(i->temp_path);
217                 if (r < 0)
218                         goto finish;
219
220                 if (rename(i->temp_path, i->final_path) < 0) {
221                         r = log_error_errno(errno, "Failed to rename to final image name: %m");
222                         goto finish;
223                 }
224
225                 free(i->temp_path);
226                 i->temp_path = NULL;
227         }
228
229         r = tar_import_make_local_copy(i);
230         if (r < 0)
231                 goto finish;
232
233         r = 0;
234
235 finish:
236         if (i->on_finished)
237                 i->on_finished(i, r, i->userdata);
238         else
239                 sd_event_exit(i->event, r);
240 }
241
242 static int tar_import_job_on_open_disk(ImportJob *j) {
243         _cleanup_close_pair_ int pipefd[2] = { -1 , -1 };
244         TarImport *i;
245         int r;
246
247         assert(j);
248         assert(j->userdata);
249
250         i = j->userdata;
251         assert(i->tar_job == j);
252         assert(!i->final_path);
253         assert(!i->temp_path);
254         assert(i->tar_pid <= 0);
255
256         r = import_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
257         if (r < 0)
258                 return log_oom();
259
260         r = tempfn_random(i->final_path, &i->temp_path);
261         if (r < 0)
262                 return log_oom();
263
264         mkdir_parents_label(i->temp_path, 0700);
265
266         r = btrfs_subvol_make(i->temp_path);
267         if (r == -ENOTTY) {
268                 if (mkdir(i->temp_path, 0755) < 0)
269                         return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
270         } else if (r < 0)
271                 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
272
273         j->disk_fd = import_fork_tar(i->temp_path, &i->tar_pid);
274         if (j->disk_fd < 0)
275                 return j->disk_fd;
276
277         return 0;
278 }
279
280 int tar_import_pull(TarImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
281         int r;
282
283         assert(i);
284
285         if (!http_url_is_valid(url))
286                 return -EINVAL;
287
288         if (local && !machine_name_is_valid(local))
289                 return -EINVAL;
290
291         if (i->tar_job)
292                 return -EBUSY;
293
294         r = free_and_strdup(&i->local, local);
295         if (r < 0)
296                 return r;
297         i->force_local = force_local;
298         i->verify = verify;
299
300         r = import_job_new(&i->tar_job, url, i->glue, i);
301         if (r < 0)
302                 return r;
303
304         i->tar_job->on_finished = tar_import_job_on_finished;
305         i->tar_job->on_open_disk = tar_import_job_on_open_disk;
306         i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
307
308         r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
309         if (r < 0)
310                 return r;
311
312         r = import_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_import_job_on_finished, i);
313         if (r < 0)
314                 return r;
315
316         r = import_job_begin(i->tar_job);
317         if (r < 0)
318                 return r;
319
320         if (i->checksum_job) {
321                 r = import_job_begin(i->checksum_job);
322                 if (r < 0)
323                         return r;
324         }
325
326         if (i->signature_job) {
327                 r = import_job_begin(i->signature_job);
328                 if (r < 0)
329                         return r;
330         }
331
332         return 0;
333 }