]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/import-tar.c
import: when downloading images, create a subtree quota group for them
[thirdparty/systemd.git] / src / import / import-tar.c
CommitLineData
b6e676ce
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 <linux/fs.h>
23
24#include "sd-daemon.h"
25#include "sd-event.h"
26#include "util.h"
27#include "path-util.h"
28#include "btrfs-util.h"
25300b5a 29#include "hostname-util.h"
b6e676ce
LP
30#include "copy.h"
31#include "mkdir.h"
c6878637 32#include "rm-rf.h"
b6e676ce
LP
33#include "ratelimit.h"
34#include "machine-pool.h"
35#include "qcow2-util.h"
36#include "import-compress.h"
37#include "import-common.h"
38#include "import-tar.h"
0b452006 39#include "process-util.h"
b6e676ce
LP
40
41struct TarImport {
42 sd_event *event;
43
44 char *image_root;
45
46 TarImportFinished on_finished;
47 void *userdata;
48
49 char *local;
50 bool force_local;
51 bool read_only;
52 bool grow_machine_directory;
53
54 char *temp_path;
55 char *final_path;
56
57 int input_fd;
58 int tar_fd;
59
60 ImportCompress compress;
61
62 uint64_t written_since_last_grow;
63
64 sd_event_source *input_event_source;
65
66 uint8_t buffer[16*1024];
67 size_t buffer_size;
68
69 uint64_t written_compressed;
70 uint64_t written_uncompressed;
71
72 struct stat st;
73
74 pid_t tar_pid;
75
76 unsigned last_percent;
77 RateLimit progress_rate_limit;
78};
79
80TarImport* tar_import_unref(TarImport *i) {
81 if (!i)
82 return NULL;
83
587fec42 84 sd_event_source_unref(i->input_event_source);
b6e676ce
LP
85
86 if (i->tar_pid > 1) {
87 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
88 (void) wait_for_terminate(i->tar_pid, NULL);
89 }
90
91 if (i->temp_path) {
d9e2daaf 92 (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
b6e676ce
LP
93 free(i->temp_path);
94 }
95
96 import_compress_free(&i->compress);
97
587fec42 98 sd_event_unref(i->event);
b6e676ce
LP
99
100 safe_close(i->tar_fd);
101
102 free(i->final_path);
103 free(i->image_root);
104 free(i->local);
105 free(i);
106
107 return NULL;
108}
109
110int tar_import_new(
111 TarImport **ret,
112 sd_event *event,
113 const char *image_root,
114 TarImportFinished on_finished,
115 void *userdata) {
116
117 _cleanup_(tar_import_unrefp) TarImport *i = NULL;
118 int r;
119
120 assert(ret);
121
122 i = new0(TarImport, 1);
123 if (!i)
124 return -ENOMEM;
125
126 i->input_fd = i->tar_fd = -1;
127 i->on_finished = on_finished;
128 i->userdata = userdata;
129
587fec42 130 RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
b6e676ce
LP
131 i->last_percent = (unsigned) -1;
132
133 i->image_root = strdup(image_root ?: "/var/lib/machines");
134 if (!i->image_root)
135 return -ENOMEM;
136
137 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
138
139 if (event)
140 i->event = sd_event_ref(event);
141 else {
142 r = sd_event_default(&i->event);
143 if (r < 0)
144 return r;
145 }
146
147 *ret = i;
148 i = NULL;
149
150 return 0;
151}
152
153static void tar_import_report_progress(TarImport *i) {
154 unsigned percent;
155 assert(i);
156
157 /* We have no size information, unless the source is a regular file */
158 if (!S_ISREG(i->st.st_mode))
159 return;
160
161 if (i->written_compressed >= (uint64_t) i->st.st_size)
162 percent = 100;
163 else
164 percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
165
166 if (percent == i->last_percent)
167 return;
168
169 if (!ratelimit_test(&i->progress_rate_limit))
170 return;
171
172 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
173 log_info("Imported %u%%.", percent);
174
175 i->last_percent = percent;
176}
177
178static int tar_import_finish(TarImport *i) {
179 int r;
180
181 assert(i);
182 assert(i->tar_fd >= 0);
183 assert(i->temp_path);
184 assert(i->final_path);
185
186 i->tar_fd = safe_close(i->tar_fd);
187
188 if (i->tar_pid > 0) {
189 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
190 i->tar_pid = 0;
191 if (r < 0)
192 return r;
193 }
194
195 if (i->read_only) {
196 r = import_make_read_only(i->temp_path);
197 if (r < 0)
198 return r;
199 }
200
d9e2daaf
LP
201 if (i->force_local)
202 (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
b6e676ce 203
f85ef957
AC
204 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
205 if (r < 0)
206 return log_error_errno(r, "Failed to move image into place: %m");
b6e676ce 207
a1e58e8e 208 i->temp_path = mfree(i->temp_path);
b6e676ce
LP
209
210 return 0;
211}
212
213static int tar_import_fork_tar(TarImport *i) {
214 int r;
215
216 assert(i);
217
218 assert(!i->final_path);
219 assert(!i->temp_path);
220 assert(i->tar_fd < 0);
221
222 i->final_path = strjoin(i->image_root, "/", i->local, NULL);
223 if (!i->final_path)
224 return log_oom();
225
14bcf25c 226 r = tempfn_random(i->final_path, NULL, &i->temp_path);
b6e676ce
LP
227 if (r < 0)
228 return log_oom();
229
230 (void) mkdir_parents_label(i->temp_path, 0700);
231
232 r = btrfs_subvol_make(i->temp_path);
233 if (r == -ENOTTY) {
234 if (mkdir(i->temp_path, 0755) < 0)
235 return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
236 } else if (r < 0)
237 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
8c9cfc28
LP
238 else
239 (void) import_assign_pool_quota_and_warn(i->temp_path);
b6e676ce 240
587fec42 241 i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
b6e676ce
LP
242 if (i->tar_fd < 0)
243 return i->tar_fd;
244
245 return 0;
246}
247
248static int tar_import_write(const void *p, size_t sz, void *userdata) {
249 TarImport *i = userdata;
250 int r;
251
252 if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) {
253 i->written_since_last_grow = 0;
254 grow_machine_directory();
255 }
256
257 r = loop_write(i->tar_fd, p, sz, false);
258 if (r < 0)
259 return r;
260
261 i->written_uncompressed += sz;
262 i->written_since_last_grow += sz;
263
264 return 0;
265}
266
267static int tar_import_process(TarImport *i) {
268 ssize_t l;
269 int r;
270
271 assert(i);
272 assert(i->buffer_size < sizeof(i->buffer));
273
274 l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size);
275 if (l < 0) {
587fec42
LP
276 if (errno == EAGAIN)
277 return 0;
278
b6e676ce
LP
279 r = log_error_errno(errno, "Failed to read input file: %m");
280 goto finish;
281 }
282 if (l == 0) {
283 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
284 log_error("Premature end of file: %m");
285 r = -EIO;
286 goto finish;
287 }
288
289 r = tar_import_finish(i);
290 goto finish;
291 }
292
293 i->buffer_size += l;
294
295 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
296 r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size);
297 if (r < 0) {
298 log_error("Failed to detect file compression: %m");
299 goto finish;
300 }
301 if (r == 0) /* Need more data */
302 return 0;
303
304 r = tar_import_fork_tar(i);
305 if (r < 0)
306 goto finish;
307 }
308
309 r = import_uncompress(&i->compress, i->buffer, i->buffer_size, tar_import_write, i);
310 if (r < 0) {
311 log_error_errno(r, "Failed to decode and write: %m");
312 goto finish;
313 }
314
315 i->written_compressed += i->buffer_size;
316 i->buffer_size = 0;
317
318 tar_import_report_progress(i);
319
320 return 0;
321
322finish:
323 if (i->on_finished)
324 i->on_finished(i, r, i->userdata);
325 else
326 sd_event_exit(i->event, r);
327
328 return 0;
329}
330
331static int tar_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
332 TarImport *i = userdata;
333
334 return tar_import_process(i);
335}
336
337static int tar_import_on_defer(sd_event_source *s, void *userdata) {
338 TarImport *i = userdata;
339
340 return tar_import_process(i);
341}
342
343int tar_import_start(TarImport *i, int fd, const char *local, bool force_local, bool read_only) {
344 int r;
345
346 assert(i);
347 assert(fd >= 0);
348 assert(local);
349
350 if (!machine_name_is_valid(local))
351 return -EINVAL;
352
353 if (i->input_fd >= 0)
354 return -EBUSY;
355
587fec42
LP
356 r = fd_nonblock(fd, true);
357 if (r < 0)
358 return r;
359
b6e676ce
LP
360 r = free_and_strdup(&i->local, local);
361 if (r < 0)
362 return r;
363 i->force_local = force_local;
364 i->read_only = read_only;
365
366 if (fstat(fd, &i->st) < 0)
367 return -errno;
368
369 r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i);
370 if (r == -EPERM) {
371 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
372 r = sd_event_add_defer(i->event, &i->input_event_source, tar_import_on_defer, i);
373 if (r < 0)
374 return r;
375
376 r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON);
377 }
378 if (r < 0)
379 return r;
380
381 i->input_fd = fd;
382 return r;
383}