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