]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/import-tar.c
update TODO
[thirdparty/systemd.git] / src / import / import-tar.c
CommitLineData
56ebfaf1
LP
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
56ebfaf1
LP
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"
3d7415f4 32#include "import-util.h"
56ebfaf1
LP
33#include "curl-util.h"
34#include "import-job.h"
3d7415f4 35#include "import-common.h"
56ebfaf1
LP
36#include "import-tar.h"
37
38struct TarImport {
39 sd_event *event;
40 CurlGlue *glue;
41
42 char *image_root;
43
44 ImportJob *tar_job;
0100b6e1
LP
45 ImportJob *checksum_job;
46 ImportJob *signature_job;
56ebfaf1
LP
47
48 TarImportFinished on_finished;
49 void *userdata;
50
56ebfaf1
LP
51 char *local;
52 bool force_local;
53
54 pid_t tar_pid;
55
56 char *temp_path;
57 char *final_path;
0100b6e1
LP
58
59 ImportVerify verify;
56ebfaf1
LP
60};
61
62TarImport* tar_import_unref(TarImport *i) {
63 if (!i)
64 return NULL;
65
0100b6e1 66 if (i->tar_pid > 1) {
8b71fce8 67 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
0100b6e1 68 (void) wait_for_terminate(i->tar_pid, NULL);
56ebfaf1
LP
69 }
70
71 import_job_unref(i->tar_job);
0100b6e1
LP
72 import_job_unref(i->checksum_job);
73 import_job_unref(i->signature_job);
56ebfaf1
LP
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);
0d6e763b 81 free(i->temp_path);
56ebfaf1
LP
82 }
83
84 free(i->final_path);
85 free(i->image_root);
86 free(i->local);
56ebfaf1
LP
87 free(i);
88
89 return NULL;
90}
91
8b71fce8
LP
92int tar_import_new(
93 TarImport **ret,
94 sd_event *event,
95 const char *image_root,
96 TarImportFinished on_finished,
97 void *userdata) {
98
56ebfaf1
LP
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
0d6e763b
LP
137static 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();
0d6e763b
LP
150 }
151
0100b6e1
LP
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
0d6e763b
LP
156 return 0;
157}
158
8b71fce8
LP
159static 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
56ebfaf1
LP
173static 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;
56ebfaf1 181 if (j->error != 0) {
0100b6e1
LP
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
56ebfaf1
LP
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
8b71fce8 197 if (!tar_import_is_done(i))
0100b6e1
LP
198 return;
199
200 j->disk_fd = safe_close(i->tar_job->disk_fd);
56ebfaf1
LP
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
0100b6e1
LP
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
56ebfaf1
LP
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 }
56ebfaf1 224
0d6e763b
LP
225 free(i->temp_path);
226 i->temp_path = NULL;
56ebfaf1
LP
227 }
228
0d6e763b
LP
229 r = tar_import_make_local_copy(i);
230 if (r < 0)
231 goto finish;
232
56ebfaf1
LP
233 r = 0;
234
235finish:
56ebfaf1
LP
236 if (i->on_finished)
237 i->on_finished(i, r, i->userdata);
238 else
239 sd_event_exit(i->event, r);
240}
241
242static 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;
8b71fce8
LP
251 assert(i->tar_job == j);
252 assert(!i->final_path);
253 assert(!i->temp_path);
254 assert(i->tar_pid <= 0);
56ebfaf1
LP
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 if (pipe2(pipefd, O_CLOEXEC) < 0)
274 return log_error_errno(errno, "Failed to create pipe for tar: %m");
275
276 i->tar_pid = fork();
277 if (i->tar_pid < 0)
278 return log_error_errno(errno, "Failed to fork off tar: %m");
279 if (i->tar_pid == 0) {
280 int null_fd;
281
0100b6e1
LP
282 /* Child */
283
56ebfaf1
LP
284 reset_all_signal_handlers();
285 reset_signal_mask();
286 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
287
288 pipefd[1] = safe_close(pipefd[1]);
289
290 if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
291 log_error_errno(errno, "Failed to dup2() fd: %m");
292 _exit(EXIT_FAILURE);
293 }
294
295 if (pipefd[0] != STDIN_FILENO)
f4c135bf 296 pipefd[0] = safe_close(pipefd[0]);
56ebfaf1
LP
297
298 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
299 if (null_fd < 0) {
300 log_error_errno(errno, "Failed to open /dev/null: %m");
301 _exit(EXIT_FAILURE);
302 }
303
304 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
305 log_error_errno(errno, "Failed to dup2() fd: %m");
306 _exit(EXIT_FAILURE);
307 }
308
309 if (null_fd != STDOUT_FILENO)
f4c135bf
LP
310 null_fd = safe_close(null_fd);
311
312 fd_cloexec(STDIN_FILENO, false);
313 fd_cloexec(STDOUT_FILENO, false);
314 fd_cloexec(STDERR_FILENO, false);
56ebfaf1
LP
315
316 execlp("tar", "tar", "--numeric-owner", "-C", i->temp_path, "-px", NULL);
5a3b1abd 317 log_error_errno(errno, "Failed to execute tar: %m");
56ebfaf1
LP
318 _exit(EXIT_FAILURE);
319 }
320
321 pipefd[0] = safe_close(pipefd[0]);
322
323 j->disk_fd = pipefd[1];
324 pipefd[1] = -1;
325
326 return 0;
327}
328
0100b6e1 329int tar_import_pull(TarImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
56ebfaf1
LP
330 int r;
331
332 assert(i);
333
56ebfaf1
LP
334 if (!http_url_is_valid(url))
335 return -EINVAL;
336
337 if (local && !machine_name_is_valid(local))
338 return -EINVAL;
339
8b71fce8
LP
340 if (i->tar_job)
341 return -EBUSY;
342
56ebfaf1
LP
343 r = free_and_strdup(&i->local, local);
344 if (r < 0)
345 return r;
56ebfaf1 346 i->force_local = force_local;
0100b6e1 347 i->verify = verify;
56ebfaf1
LP
348
349 r = import_job_new(&i->tar_job, url, i->glue, i);
350 if (r < 0)
351 return r;
352
353 i->tar_job->on_finished = tar_import_job_on_finished;
354 i->tar_job->on_open_disk = tar_import_job_on_open_disk;
0100b6e1 355 i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
56ebfaf1
LP
356
357 r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
358 if (r < 0)
359 return r;
360
0100b6e1
LP
361 r = import_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_import_job_on_finished, i);
362 if (r < 0)
363 return r;
364
365 r = import_job_begin(i->tar_job);
366 if (r < 0)
367 return r;
368
369 if (i->checksum_job) {
370 r = import_job_begin(i->checksum_job);
371 if (r < 0)
372 return r;
373 }
374
375 if (i->signature_job) {
376 r = import_job_begin(i->signature_job);
377 if (r < 0)
378 return r;
379 }
380
381 return 0;
56ebfaf1 382}