2 This file is part of systemd.
4 Copyright 2015 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "sd-daemon.h"
25 #include "alloc-util.h"
26 #include "btrfs-util.h"
27 #include "chattr-util.h"
32 #include "hostname-util.h"
33 #include "import-common.h"
34 #include "import-compress.h"
35 #include "import-raw.h"
37 #include "machine-pool.h"
39 #include "path-util.h"
40 #include "qcow2-util.h"
41 #include "ratelimit.h"
43 #include "string-util.h"
51 RawImportFinished on_finished
;
57 bool grow_machine_directory
;
65 ImportCompress compress
;
67 uint64_t written_since_last_grow
;
69 sd_event_source
*input_event_source
;
71 uint8_t buffer
[16*1024];
74 uint64_t written_compressed
;
75 uint64_t written_uncompressed
;
79 unsigned last_percent
;
80 RateLimit progress_rate_limit
;
83 RawImport
* raw_import_unref(RawImport
*i
) {
87 sd_event_unref(i
->event
);
90 (void) unlink(i
->temp_path
);
94 import_compress_free(&i
->compress
);
96 sd_event_source_unref(i
->input_event_source
);
98 safe_close(i
->output_fd
);
111 const char *image_root
,
112 RawImportFinished on_finished
,
115 _cleanup_(raw_import_unrefp
) RawImport
*i
= NULL
;
120 i
= new0(RawImport
, 1);
124 i
->input_fd
= i
->output_fd
= -1;
125 i
->on_finished
= on_finished
;
126 i
->userdata
= userdata
;
128 RATELIMIT_INIT(i
->progress_rate_limit
, 100 * USEC_PER_MSEC
, 1);
129 i
->last_percent
= (unsigned) -1;
131 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
135 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
138 i
->event
= sd_event_ref(event
);
140 r
= sd_event_default(&i
->event
);
151 static void raw_import_report_progress(RawImport
*i
) {
155 /* We have no size information, unless the source is a regular file */
156 if (!S_ISREG(i
->st
.st_mode
))
159 if (i
->written_compressed
>= (uint64_t) i
->st
.st_size
)
162 percent
= (unsigned) ((i
->written_compressed
* UINT64_C(100)) / (uint64_t) i
->st
.st_size
);
164 if (percent
== i
->last_percent
)
167 if (!ratelimit_test(&i
->progress_rate_limit
))
170 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
171 log_info("Imported %u%%.", percent
);
173 i
->last_percent
= percent
;
176 static int raw_import_maybe_convert_qcow2(RawImport
*i
) {
177 _cleanup_close_
int converted_fd
= -1;
178 _cleanup_free_
char *t
= NULL
;
183 r
= qcow2_detect(i
->output_fd
);
185 return log_error_errno(r
, "Failed to detect whether this is a QCOW2 image: %m");
189 /* This is a QCOW2 image, let's convert it */
190 r
= tempfn_random(i
->final_path
, NULL
, &t
);
194 converted_fd
= open(t
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
195 if (converted_fd
< 0)
196 return log_error_errno(errno
, "Failed to create %s: %m", t
);
198 r
= chattr_fd(converted_fd
, FS_NOCOW_FL
, FS_NOCOW_FL
);
200 log_warning_errno(r
, "Failed to set file attributes on %s: %m", t
);
202 log_info("Unpacking QCOW2 file.");
204 r
= qcow2_convert(i
->output_fd
, converted_fd
);
207 return log_error_errno(r
, "Failed to convert qcow2 image: %m");
210 (void) unlink(i
->temp_path
);
215 safe_close(i
->output_fd
);
216 i
->output_fd
= converted_fd
;
222 static int raw_import_finish(RawImport
*i
) {
226 assert(i
->output_fd
>= 0);
227 assert(i
->temp_path
);
228 assert(i
->final_path
);
230 /* In case this was a sparse file, make sure the file system is right */
231 if (i
->written_uncompressed
> 0) {
232 if (ftruncate(i
->output_fd
, i
->written_uncompressed
) < 0)
233 return log_error_errno(errno
, "Failed to truncate file: %m");
236 r
= raw_import_maybe_convert_qcow2(i
);
240 if (S_ISREG(i
->st
.st_mode
)) {
241 (void) copy_times(i
->input_fd
, i
->output_fd
);
242 (void) copy_xattr(i
->input_fd
, i
->output_fd
);
246 r
= import_make_read_only_fd(i
->output_fd
);
252 (void) rm_rf(i
->final_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
254 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
256 return log_error_errno(r
, "Failed to move image into place: %m");
258 i
->temp_path
= mfree(i
->temp_path
);
263 static int raw_import_open_disk(RawImport
*i
) {
268 assert(!i
->final_path
);
269 assert(!i
->temp_path
);
270 assert(i
->output_fd
< 0);
272 i
->final_path
= strjoin(i
->image_root
, "/", i
->local
, ".raw", NULL
);
276 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
280 (void) mkdir_parents_label(i
->temp_path
, 0700);
282 i
->output_fd
= open(i
->temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
283 if (i
->output_fd
< 0)
284 return log_error_errno(errno
, "Failed to open destination %s: %m", i
->temp_path
);
286 r
= chattr_fd(i
->output_fd
, FS_NOCOW_FL
, FS_NOCOW_FL
);
288 log_warning_errno(r
, "Failed to set file attributes on %s: %m", i
->temp_path
);
293 static int raw_import_try_reflink(RawImport
*i
) {
298 assert(i
->input_fd
>= 0);
299 assert(i
->output_fd
>= 0);
301 if (i
->compress
.type
!= IMPORT_COMPRESS_UNCOMPRESSED
)
304 if (!S_ISREG(i
->st
.st_mode
))
307 p
= lseek(i
->input_fd
, 0, SEEK_CUR
);
309 return log_error_errno(errno
, "Failed to read file offset of input file: %m");
311 /* Let's only try a btrfs reflink, if we are reading from the beginning of the file */
312 if ((uint64_t) p
!= (uint64_t) i
->buffer_size
)
315 r
= btrfs_reflink(i
->input_fd
, i
->output_fd
);
322 static int raw_import_write(const void *p
, size_t sz
, void *userdata
) {
323 RawImport
*i
= userdata
;
326 if (i
->grow_machine_directory
&& i
->written_since_last_grow
>= GROW_INTERVAL_BYTES
) {
327 i
->written_since_last_grow
= 0;
328 grow_machine_directory();
331 n
= sparse_write(i
->output_fd
, p
, sz
, 64);
337 i
->written_uncompressed
+= sz
;
338 i
->written_since_last_grow
+= sz
;
343 static int raw_import_process(RawImport
*i
) {
348 assert(i
->buffer_size
< sizeof(i
->buffer
));
350 l
= read(i
->input_fd
, i
->buffer
+ i
->buffer_size
, sizeof(i
->buffer
) - i
->buffer_size
);
355 r
= log_error_errno(errno
, "Failed to read input file: %m");
359 if (i
->compress
.type
== IMPORT_COMPRESS_UNKNOWN
) {
360 log_error("Premature end of file: %m");
365 r
= raw_import_finish(i
);
371 if (i
->compress
.type
== IMPORT_COMPRESS_UNKNOWN
) {
372 r
= import_uncompress_detect(&i
->compress
, i
->buffer
, i
->buffer_size
);
374 log_error("Failed to detect file compression: %m");
377 if (r
== 0) /* Need more data */
380 r
= raw_import_open_disk(i
);
384 r
= raw_import_try_reflink(i
);
388 r
= raw_import_finish(i
);
393 r
= import_uncompress(&i
->compress
, i
->buffer
, i
->buffer_size
, raw_import_write
, i
);
395 log_error_errno(r
, "Failed to decode and write: %m");
399 i
->written_compressed
+= i
->buffer_size
;
402 raw_import_report_progress(i
);
408 i
->on_finished(i
, r
, i
->userdata
);
410 sd_event_exit(i
->event
, r
);
415 static int raw_import_on_input(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
416 RawImport
*i
= userdata
;
418 return raw_import_process(i
);
421 static int raw_import_on_defer(sd_event_source
*s
, void *userdata
) {
422 RawImport
*i
= userdata
;
424 return raw_import_process(i
);
427 int raw_import_start(RawImport
*i
, int fd
, const char *local
, bool force_local
, bool read_only
) {
434 if (!machine_name_is_valid(local
))
437 if (i
->input_fd
>= 0)
440 r
= fd_nonblock(fd
, true);
444 r
= free_and_strdup(&i
->local
, local
);
447 i
->force_local
= force_local
;
448 i
->read_only
= read_only
;
450 if (fstat(fd
, &i
->st
) < 0)
453 r
= sd_event_add_io(i
->event
, &i
->input_event_source
, fd
, EPOLLIN
, raw_import_on_input
, i
);
455 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
456 r
= sd_event_add_defer(i
->event
, &i
->input_event_source
, raw_import_on_defer
, i
);
460 r
= sd_event_source_set_enabled(i
->input_event_source
, SD_EVENT_ON
);