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