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