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