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