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