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