]> git.proxmox.com Git - systemd.git/blame - src/import/import-raw.c
New upstream version 236
[systemd.git] / src / import / import-raw.c
CommitLineData
52ad194e 1/* SPDX-License-Identifier: LGPL-2.1+ */
e735f4d4
MP
2/***
3 This file is part of systemd.
4
e3bff60a 5 Copyright 2015 Lennart Poettering
e735f4d4
MP
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
e735f4d4 21#include <linux/fs.h>
e735f4d4
MP
22
23#include "sd-daemon.h"
e3bff60a 24#include "sd-event.h"
db2df898
MP
25
26#include "alloc-util.h"
e3bff60a 27#include "btrfs-util.h"
db2df898 28#include "chattr-util.h"
e3bff60a 29#include "copy.h"
db2df898
MP
30#include "fd-util.h"
31#include "fileio.h"
32#include "fs-util.h"
33#include "hostname-util.h"
e735f4d4 34#include "import-common.h"
db2df898 35#include "import-compress.h"
e735f4d4 36#include "import-raw.h"
db2df898
MP
37#include "io-util.h"
38#include "machine-pool.h"
39#include "mkdir.h"
40#include "path-util.h"
41#include "qcow2-util.h"
42#include "ratelimit.h"
43#include "rm-rf.h"
44#include "string-util.h"
45#include "util.h"
e735f4d4 46
e735f4d4
MP
47struct RawImport {
48 sd_event *event;
e735f4d4
MP
49
50 char *image_root;
51
e735f4d4
MP
52 RawImportFinished on_finished;
53 void *userdata;
54
55 char *local;
56 bool force_local;
e3bff60a
MP
57 bool read_only;
58 bool grow_machine_directory;
e735f4d4
MP
59
60 char *temp_path;
61 char *final_path;
62
e3bff60a
MP
63 int input_fd;
64 int output_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 unsigned last_percent;
81 RateLimit progress_rate_limit;
e735f4d4
MP
82};
83
84RawImport* raw_import_unref(RawImport *i) {
85 if (!i)
86 return NULL;
87
e735f4d4
MP
88 sd_event_unref(i->event);
89
90 if (i->temp_path) {
91 (void) unlink(i->temp_path);
92 free(i->temp_path);
93 }
94
e3bff60a
MP
95 import_compress_free(&i->compress);
96
97 sd_event_source_unref(i->input_event_source);
98
99 safe_close(i->output_fd);
100
e735f4d4
MP
101 free(i->final_path);
102 free(i->image_root);
103 free(i->local);
8a584da2 104 return mfree(i);
e735f4d4
MP
105}
106
107int raw_import_new(
108 RawImport **ret,
109 sd_event *event,
110 const char *image_root,
111 RawImportFinished on_finished,
112 void *userdata) {
113
114 _cleanup_(raw_import_unrefp) RawImport *i = NULL;
115 int r;
116
117 assert(ret);
118
119 i = new0(RawImport, 1);
120 if (!i)
121 return -ENOMEM;
122
e3bff60a 123 i->input_fd = i->output_fd = -1;
e735f4d4
MP
124 i->on_finished = on_finished;
125 i->userdata = userdata;
126
e3bff60a
MP
127 RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
128 i->last_percent = (unsigned) -1;
129
e735f4d4
MP
130 i->image_root = strdup(image_root ?: "/var/lib/machines");
131 if (!i->image_root)
132 return -ENOMEM;
133
e3bff60a
MP
134 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
135
e735f4d4
MP
136 if (event)
137 i->event = sd_event_ref(event);
138 else {
139 r = sd_event_default(&i->event);
140 if (r < 0)
141 return r;
142 }
143
e735f4d4
MP
144 *ret = i;
145 i = NULL;
146
147 return 0;
148}
149
e3bff60a 150static void raw_import_report_progress(RawImport *i) {
e735f4d4 151 unsigned percent;
e735f4d4
MP
152 assert(i);
153
e3bff60a
MP
154 /* We have no size information, unless the source is a regular file */
155 if (!S_ISREG(i->st.st_mode))
156 return;
e735f4d4 157
e3bff60a
MP
158 if (i->written_compressed >= (uint64_t) i->st.st_size)
159 percent = 100;
160 else
161 percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
e735f4d4 162
e3bff60a
MP
163 if (percent == i->last_percent)
164 return;
e735f4d4 165
e3bff60a
MP
166 if (!ratelimit_test(&i->progress_rate_limit))
167 return;
e735f4d4
MP
168
169 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
e3bff60a
MP
170 log_info("Imported %u%%.", percent);
171
172 i->last_percent = percent;
e735f4d4
MP
173}
174
175static int raw_import_maybe_convert_qcow2(RawImport *i) {
176 _cleanup_close_ int converted_fd = -1;
177 _cleanup_free_ char *t = NULL;
178 int r;
179
180 assert(i);
e735f4d4 181
e3bff60a 182 r = qcow2_detect(i->output_fd);
e735f4d4
MP
183 if (r < 0)
184 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
185 if (r == 0)
186 return 0;
187
188 /* This is a QCOW2 image, let's convert it */
86f210e9 189 r = tempfn_random(i->final_path, NULL, &t);
e735f4d4
MP
190 if (r < 0)
191 return log_oom();
192
e3bff60a 193 converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
e735f4d4
MP
194 if (converted_fd < 0)
195 return log_error_errno(errno, "Failed to create %s: %m", t);
196
e3bff60a 197 r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
e735f4d4 198 if (r < 0)
db2df898 199 log_warning_errno(r, "Failed to set file attributes on %s: %m", t);
e735f4d4
MP
200
201 log_info("Unpacking QCOW2 file.");
202
e3bff60a 203 r = qcow2_convert(i->output_fd, converted_fd);
e735f4d4
MP
204 if (r < 0) {
205 unlink(t);
206 return log_error_errno(r, "Failed to convert qcow2 image: %m");
207 }
208
e3bff60a 209 (void) unlink(i->temp_path);
52ad194e 210 free_and_replace(i->temp_path, t);
e735f4d4 211
e3bff60a
MP
212 safe_close(i->output_fd);
213 i->output_fd = converted_fd;
e735f4d4
MP
214 converted_fd = -1;
215
216 return 1;
217}
218
e3bff60a 219static int raw_import_finish(RawImport *i) {
e735f4d4
MP
220 int r;
221
222 assert(i);
e3bff60a
MP
223 assert(i->output_fd >= 0);
224 assert(i->temp_path);
225 assert(i->final_path);
226
227 /* In case this was a sparse file, make sure the file system is right */
228 if (i->written_uncompressed > 0) {
229 if (ftruncate(i->output_fd, i->written_uncompressed) < 0)
230 return log_error_errno(errno, "Failed to truncate file: %m");
231 }
e735f4d4 232
e3bff60a
MP
233 r = raw_import_maybe_convert_qcow2(i);
234 if (r < 0)
235 return r;
e735f4d4 236
e3bff60a
MP
237 if (S_ISREG(i->st.st_mode)) {
238 (void) copy_times(i->input_fd, i->output_fd);
239 (void) copy_xattr(i->input_fd, i->output_fd);
240 }
e735f4d4 241
e3bff60a
MP
242 if (i->read_only) {
243 r = import_make_read_only_fd(i->output_fd);
244 if (r < 0)
245 return r;
246 }
e735f4d4 247
e3bff60a
MP
248 if (i->force_local)
249 (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
e735f4d4 250
e3bff60a
MP
251 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
252 if (r < 0)
253 return log_error_errno(r, "Failed to move image into place: %m");
e735f4d4 254
6300502b 255 i->temp_path = mfree(i->temp_path);
e735f4d4 256
e3bff60a
MP
257 return 0;
258}
e735f4d4 259
e3bff60a
MP
260static int raw_import_open_disk(RawImport *i) {
261 int r;
e735f4d4 262
e3bff60a 263 assert(i);
e735f4d4 264
e3bff60a
MP
265 assert(!i->final_path);
266 assert(!i->temp_path);
267 assert(i->output_fd < 0);
268
2897b343 269 i->final_path = strjoin(i->image_root, "/", i->local, ".raw");
e3bff60a
MP
270 if (!i->final_path)
271 return log_oom();
272
86f210e9 273 r = tempfn_random(i->final_path, NULL, &i->temp_path);
e735f4d4
MP
274 if (r < 0)
275 return log_oom();
276
e3bff60a 277 (void) mkdir_parents_label(i->temp_path, 0700);
e735f4d4 278
e3bff60a
MP
279 i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
280 if (i->output_fd < 0)
281 return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path);
282
283 r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL);
e735f4d4 284 if (r < 0)
db2df898 285 log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path);
e735f4d4 286
e3bff60a
MP
287 return 0;
288}
e735f4d4 289
e3bff60a
MP
290static int raw_import_try_reflink(RawImport *i) {
291 off_t p;
292 int r;
e735f4d4 293
e3bff60a
MP
294 assert(i);
295 assert(i->input_fd >= 0);
296 assert(i->output_fd >= 0);
e735f4d4 297
e3bff60a
MP
298 if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED)
299 return 0;
300
301 if (!S_ISREG(i->st.st_mode))
302 return 0;
303
304 p = lseek(i->input_fd, 0, SEEK_CUR);
305 if (p == (off_t) -1)
306 return log_error_errno(errno, "Failed to read file offset of input file: %m");
307
308 /* Let's only try a btrfs reflink, if we are reading from the beginning of the file */
309 if ((uint64_t) p != (uint64_t) i->buffer_size)
310 return 0;
311
312 r = btrfs_reflink(i->input_fd, i->output_fd);
313 if (r >= 0)
314 return 1;
e735f4d4 315
e735f4d4
MP
316 return 0;
317}
318
e3bff60a
MP
319static int raw_import_write(const void *p, size_t sz, void *userdata) {
320 RawImport *i = userdata;
321 ssize_t n;
322
323 if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) {
324 i->written_since_last_grow = 0;
325 grow_machine_directory();
326 }
e735f4d4 327
e3bff60a
MP
328 n = sparse_write(i->output_fd, p, sz, 64);
329 if (n < 0)
330 return -errno;
331 if ((size_t) n < sz)
332 return -EIO;
e735f4d4 333
e3bff60a
MP
334 i->written_uncompressed += sz;
335 i->written_since_last_grow += sz;
336
337 return 0;
e735f4d4
MP
338}
339
e3bff60a
MP
340static int raw_import_process(RawImport *i) {
341 ssize_t l;
e735f4d4
MP
342 int r;
343
e3bff60a
MP
344 assert(i);
345 assert(i->buffer_size < sizeof(i->buffer));
e735f4d4 346
e3bff60a
MP
347 l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size);
348 if (l < 0) {
349 if (errno == EAGAIN)
350 return 0;
e735f4d4 351
e3bff60a 352 r = log_error_errno(errno, "Failed to read input file: %m");
e735f4d4
MP
353 goto finish;
354 }
e3bff60a
MP
355 if (l == 0) {
356 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
81c58355 357 log_error("Premature end of file.");
e3bff60a
MP
358 r = -EIO;
359 goto finish;
360 }
e735f4d4 361
e3bff60a
MP
362 r = raw_import_finish(i);
363 goto finish;
364 }
e735f4d4 365
e3bff60a 366 i->buffer_size += l;
e735f4d4 367
e3bff60a
MP
368 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
369 r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size);
370 if (r < 0) {
81c58355 371 log_error_errno(r, "Failed to detect file compression: %m");
e735f4d4 372 goto finish;
e3bff60a
MP
373 }
374 if (r == 0) /* Need more data */
375 return 0;
e735f4d4 376
e3bff60a 377 r = raw_import_open_disk(i);
e735f4d4
MP
378 if (r < 0)
379 goto finish;
380
e3bff60a 381 r = raw_import_try_reflink(i);
e735f4d4
MP
382 if (r < 0)
383 goto finish;
e3bff60a
MP
384 if (r > 0) {
385 r = raw_import_finish(i);
e735f4d4
MP
386 goto finish;
387 }
e3bff60a 388 }
e735f4d4 389
e3bff60a
MP
390 r = import_uncompress(&i->compress, i->buffer, i->buffer_size, raw_import_write, i);
391 if (r < 0) {
392 log_error_errno(r, "Failed to decode and write: %m");
393 goto finish;
e735f4d4
MP
394 }
395
e3bff60a
MP
396 i->written_compressed += i->buffer_size;
397 i->buffer_size = 0;
e735f4d4 398
e3bff60a 399 raw_import_report_progress(i);
e735f4d4 400
e3bff60a 401 return 0;
e735f4d4
MP
402
403finish:
404 if (i->on_finished)
405 i->on_finished(i, r, i->userdata);
406 else
407 sd_event_exit(i->event, r);
e735f4d4
MP
408
409 return 0;
410}
411
e3bff60a
MP
412static int raw_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
413 RawImport *i = userdata;
e735f4d4 414
e3bff60a
MP
415 return raw_import_process(i);
416}
e735f4d4 417
e3bff60a
MP
418static int raw_import_on_defer(sd_event_source *s, void *userdata) {
419 RawImport *i = userdata;
e735f4d4 420
e3bff60a 421 return raw_import_process(i);
e735f4d4
MP
422}
423
e3bff60a 424int raw_import_start(RawImport *i, int fd, const char *local, bool force_local, bool read_only) {
e735f4d4
MP
425 int r;
426
427 assert(i);
e3bff60a
MP
428 assert(fd >= 0);
429 assert(local);
e735f4d4 430
e3bff60a 431 if (!machine_name_is_valid(local))
e735f4d4
MP
432 return -EINVAL;
433
e3bff60a 434 if (i->input_fd >= 0)
e735f4d4
MP
435 return -EBUSY;
436
e3bff60a 437 r = fd_nonblock(fd, true);
e735f4d4
MP
438 if (r < 0)
439 return r;
e735f4d4 440
e3bff60a 441 r = free_and_strdup(&i->local, local);
e735f4d4
MP
442 if (r < 0)
443 return r;
e3bff60a
MP
444 i->force_local = force_local;
445 i->read_only = read_only;
e735f4d4 446
e3bff60a
MP
447 if (fstat(fd, &i->st) < 0)
448 return -errno;
e735f4d4 449
e3bff60a
MP
450 r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, raw_import_on_input, i);
451 if (r == -EPERM) {
452 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
453 r = sd_event_add_defer(i->event, &i->input_event_source, raw_import_on_defer, i);
e735f4d4
MP
454 if (r < 0)
455 return r;
e735f4d4 456
e3bff60a 457 r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON);
e735f4d4 458 }
e3bff60a
MP
459 if (r < 0)
460 return r;
e735f4d4 461
e3bff60a
MP
462 i->input_fd = fd;
463 return r;
e735f4d4 464}