]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/import-tar.c
importd: automatically grow /var/lib/machines/ loopback filesystem during downloads
[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 "sd-daemon.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 "path-util.h"
34 #include "import-util.h"
35 #include "curl-util.h"
36 #include "import-job.h"
37 #include "import-common.h"
38 #include "import-tar.h"
39
40 typedef enum TarProgress {
41 TAR_DOWNLOADING,
42 TAR_VERIFYING,
43 TAR_FINALIZING,
44 TAR_COPYING,
45 } TarProgress;
46
47 struct TarImport {
48 sd_event *event;
49 CurlGlue *glue;
50
51 char *image_root;
52
53 ImportJob *tar_job;
54 ImportJob *checksum_job;
55 ImportJob *signature_job;
56
57 TarImportFinished on_finished;
58 void *userdata;
59
60 char *local;
61 bool force_local;
62 bool grow_machine_directory;
63
64 pid_t tar_pid;
65
66 char *temp_path;
67 char *final_path;
68
69 ImportVerify verify;
70 };
71
72 TarImport* tar_import_unref(TarImport *i) {
73 if (!i)
74 return NULL;
75
76 if (i->tar_pid > 1) {
77 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
78 (void) wait_for_terminate(i->tar_pid, NULL);
79 }
80
81 import_job_unref(i->tar_job);
82 import_job_unref(i->checksum_job);
83 import_job_unref(i->signature_job);
84
85 curl_glue_unref(i->glue);
86 sd_event_unref(i->event);
87
88 if (i->temp_path) {
89 (void) btrfs_subvol_remove(i->temp_path);
90 (void) rm_rf_dangerous(i->temp_path, false, true, false);
91 free(i->temp_path);
92 }
93
94 free(i->final_path);
95 free(i->image_root);
96 free(i->local);
97 free(i);
98
99 return NULL;
100 }
101
102 int tar_import_new(
103 TarImport **ret,
104 sd_event *event,
105 const char *image_root,
106 TarImportFinished on_finished,
107 void *userdata) {
108
109 _cleanup_(tar_import_unrefp) TarImport *i = NULL;
110 int r;
111
112 assert(ret);
113 assert(event);
114
115 i = new0(TarImport, 1);
116 if (!i)
117 return -ENOMEM;
118
119 i->on_finished = on_finished;
120 i->userdata = userdata;
121
122 i->image_root = strdup(image_root ?: "/var/lib/machines");
123 if (!i->image_root)
124 return -ENOMEM;
125
126 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
127
128 if (event)
129 i->event = sd_event_ref(event);
130 else {
131 r = sd_event_default(&i->event);
132 if (r < 0)
133 return r;
134 }
135
136 r = curl_glue_new(&i->glue, i->event);
137 if (r < 0)
138 return r;
139
140 i->glue->on_finished = import_job_curl_on_finished;
141 i->glue->userdata = i;
142
143 *ret = i;
144 i = NULL;
145
146 return 0;
147 }
148
149 static void tar_import_report_progress(TarImport *i, TarProgress p) {
150 unsigned percent;
151
152 assert(i);
153
154 switch (p) {
155
156 case TAR_DOWNLOADING: {
157 unsigned remain = 85;
158
159 percent = 0;
160
161 if (i->checksum_job) {
162 percent += i->checksum_job->progress_percent * 5 / 100;
163 remain -= 5;
164 }
165
166 if (i->signature_job) {
167 percent += i->signature_job->progress_percent * 5 / 100;
168 remain -= 5;
169 }
170
171 if (i->tar_job)
172 percent += i->tar_job->progress_percent * remain / 100;
173 break;
174 }
175
176 case TAR_VERIFYING:
177 percent = 85;
178 break;
179
180 case TAR_FINALIZING:
181 percent = 90;
182 break;
183
184 case TAR_COPYING:
185 percent = 95;
186 break;
187
188 default:
189 assert_not_reached("Unknown progress state");
190 }
191
192 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
193 log_debug("Combined progress %u%%", percent);
194 }
195
196 static int tar_import_make_local_copy(TarImport *i) {
197 int r;
198
199 assert(i);
200 assert(i->tar_job);
201
202 if (!i->local)
203 return 0;
204
205 if (!i->final_path) {
206 r = import_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
207 if (r < 0)
208 return log_oom();
209 }
210
211 r = import_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
212 if (r < 0)
213 return r;
214
215 return 0;
216 }
217
218 static bool tar_import_is_done(TarImport *i) {
219 assert(i);
220 assert(i->tar_job);
221
222 if (i->tar_job->state != IMPORT_JOB_DONE)
223 return false;
224 if (i->checksum_job && i->checksum_job->state != IMPORT_JOB_DONE)
225 return false;
226 if (i->signature_job && i->signature_job->state != IMPORT_JOB_DONE)
227 return false;
228
229 return true;
230 }
231
232 static void tar_import_job_on_finished(ImportJob *j) {
233 TarImport *i;
234 int r;
235
236 assert(j);
237 assert(j->userdata);
238
239 i = j->userdata;
240 if (j->error != 0) {
241 if (j == i->checksum_job)
242 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
243 else if (j == i->signature_job)
244 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
245 else
246 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
247
248 r = j->error;
249 goto finish;
250 }
251
252 /* This is invoked if either the download completed
253 * successfully, or the download was skipped because we
254 * already have the etag. */
255
256 if (!tar_import_is_done(i))
257 return;
258
259 j->disk_fd = safe_close(i->tar_job->disk_fd);
260
261 if (i->tar_pid > 0) {
262 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
263 i->tar_pid = 0;
264 if (r < 0)
265 goto finish;
266 }
267
268 if (!i->tar_job->etag_exists) {
269 /* This is a new download, verify it, and move it into place */
270
271 tar_import_report_progress(i, TAR_VERIFYING);
272
273 r = import_verify(i->tar_job, i->checksum_job, i->signature_job);
274 if (r < 0)
275 goto finish;
276
277 tar_import_report_progress(i, TAR_FINALIZING);
278
279 r = import_make_read_only(i->temp_path);
280 if (r < 0)
281 goto finish;
282
283 if (rename(i->temp_path, i->final_path) < 0) {
284 r = log_error_errno(errno, "Failed to rename to final image name: %m");
285 goto finish;
286 }
287
288 free(i->temp_path);
289 i->temp_path = NULL;
290 }
291
292 tar_import_report_progress(i, TAR_COPYING);
293
294 r = tar_import_make_local_copy(i);
295 if (r < 0)
296 goto finish;
297
298 r = 0;
299
300 finish:
301 if (i->on_finished)
302 i->on_finished(i, r, i->userdata);
303 else
304 sd_event_exit(i->event, r);
305 }
306
307 static int tar_import_job_on_open_disk(ImportJob *j) {
308 TarImport *i;
309 int r;
310
311 assert(j);
312 assert(j->userdata);
313
314 i = j->userdata;
315 assert(i->tar_job == j);
316 assert(!i->final_path);
317 assert(!i->temp_path);
318 assert(i->tar_pid <= 0);
319
320 r = import_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
321 if (r < 0)
322 return log_oom();
323
324 r = tempfn_random(i->final_path, &i->temp_path);
325 if (r < 0)
326 return log_oom();
327
328 mkdir_parents_label(i->temp_path, 0700);
329
330 r = btrfs_subvol_make(i->temp_path);
331 if (r == -ENOTTY) {
332 if (mkdir(i->temp_path, 0755) < 0)
333 return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
334 } else if (r < 0)
335 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
336
337 j->disk_fd = import_fork_tar(i->temp_path, &i->tar_pid);
338 if (j->disk_fd < 0)
339 return j->disk_fd;
340
341 return 0;
342 }
343
344 static void tar_import_job_on_progress(ImportJob *j) {
345 TarImport *i;
346
347 assert(j);
348 assert(j->userdata);
349
350 i = j->userdata;
351
352 tar_import_report_progress(i, TAR_DOWNLOADING);
353 }
354
355 int tar_import_pull(TarImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
356 int r;
357
358 assert(i);
359
360 if (!http_url_is_valid(url))
361 return -EINVAL;
362
363 if (local && !machine_name_is_valid(local))
364 return -EINVAL;
365
366 if (i->tar_job)
367 return -EBUSY;
368
369 r = free_and_strdup(&i->local, local);
370 if (r < 0)
371 return r;
372 i->force_local = force_local;
373 i->verify = verify;
374
375 r = import_job_new(&i->tar_job, url, i->glue, i);
376 if (r < 0)
377 return r;
378
379 i->tar_job->on_finished = tar_import_job_on_finished;
380 i->tar_job->on_open_disk = tar_import_job_on_open_disk;
381 i->tar_job->on_progress = tar_import_job_on_progress;
382 i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
383 i->tar_job->grow_machine_directory = i->grow_machine_directory;
384
385 r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
386 if (r < 0)
387 return r;
388
389 r = import_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_import_job_on_finished, i);
390 if (r < 0)
391 return r;
392
393 r = import_job_begin(i->tar_job);
394 if (r < 0)
395 return r;
396
397 if (i->checksum_job) {
398 i->checksum_job->on_progress = tar_import_job_on_progress;
399
400 r = import_job_begin(i->checksum_job);
401 if (r < 0)
402 return r;
403 }
404
405 if (i->signature_job) {
406 i->signature_job->on_progress = tar_import_job_on_progress;
407
408 r = import_job_begin(i->signature_job);
409 if (r < 0)
410 return r;
411 }
412
413 return 0;
414 }