]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/import-tar.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / import / import-tar.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
b6e676ce
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <linux/fs.h>
22
23#include "sd-daemon.h"
24#include "sd-event.h"
07630cea 25
b5efdb8a 26#include "alloc-util.h"
b6e676ce
LP
27#include "btrfs-util.h"
28#include "copy.h"
3ffd4af2 29#include "fd-util.h"
0d39fa9c 30#include "fileio.h"
f4f15635 31#include "fs-util.h"
07630cea
LP
32#include "hostname-util.h"
33#include "import-common.h"
34#include "import-compress.h"
3ffd4af2 35#include "import-tar.h"
c004493c 36#include "io-util.h"
b6e676ce 37#include "machine-pool.h"
07630cea
LP
38#include "mkdir.h"
39#include "path-util.h"
40#include "process-util.h"
b6e676ce 41#include "qcow2-util.h"
07630cea
LP
42#include "ratelimit.h"
43#include "rm-rf.h"
44#include "string-util.h"
45#include "util.h"
b6e676ce
LP
46
47struct TarImport {
48 sd_event *event;
49
50 char *image_root;
51
52 TarImportFinished on_finished;
53 void *userdata;
54
55 char *local;
56 bool force_local;
57 bool read_only;
58 bool grow_machine_directory;
59
60 char *temp_path;
61 char *final_path;
62
63 int input_fd;
64 int tar_fd;
65
66 ImportCompress compress;
67
68 uint64_t written_since_last_grow;
69
70 sd_event_source *input_event_source;
71
72 uint8_t buffer[16*1024];
73 size_t buffer_size;
74
75 uint64_t written_compressed;
76 uint64_t written_uncompressed;
77
78 struct stat st;
79
80 pid_t tar_pid;
81
82 unsigned last_percent;
83 RateLimit progress_rate_limit;
84};
85
86TarImport* tar_import_unref(TarImport *i) {
87 if (!i)
88 return NULL;
89
587fec42 90 sd_event_source_unref(i->input_event_source);
b6e676ce
LP
91
92 if (i->tar_pid > 1) {
93 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
94 (void) wait_for_terminate(i->tar_pid, NULL);
95 }
96
97 if (i->temp_path) {
d9e2daaf 98 (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
b6e676ce
LP
99 free(i->temp_path);
100 }
101
102 import_compress_free(&i->compress);
103
587fec42 104 sd_event_unref(i->event);
b6e676ce
LP
105
106 safe_close(i->tar_fd);
107
108 free(i->final_path);
109 free(i->image_root);
110 free(i->local);
6b430fdb 111 return mfree(i);
b6e676ce
LP
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
1cc6c93a 151 *ret = TAKE_PTR(i);
b6e676ce
LP
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) {
7d4904fe 192 r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG);
b6e676ce
LP
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
605405c6 225 i->final_path = strjoin(i->image_root, "/", i->local);
b6e676ce
LP
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)
709f6e46 240 return log_error_errno(r, "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) {
35bca925 287 log_error("Premature end of file.");
b6e676ce
LP
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) {
35bca925 301 log_error_errno(r, "Failed to detect file compression: %m");
b6e676ce
LP
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}