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