1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "alloc-util.h"
9 #include "btrfs-util.h"
13 #include "hostname-util.h"
14 #include "import-common.h"
15 #include "import-compress.h"
16 #include "import-raw.h"
18 #include "machine-pool.h"
20 #include "path-util.h"
21 #include "qcow2-util.h"
22 #include "ratelimit.h"
24 #include "string-util.h"
25 #include "tmpfile-util.h"
33 RawImportFinished on_finished
;
45 ImportCompress compress
;
47 sd_event_source
*input_event_source
;
49 uint8_t buffer
[16*1024];
52 uint64_t written_compressed
;
53 uint64_t written_uncompressed
;
57 unsigned last_percent
;
58 RateLimit progress_ratelimit
;
61 RawImport
* raw_import_unref(RawImport
*i
) {
65 sd_event_unref(i
->event
);
67 unlink_and_free(i
->temp_path
);
69 import_compress_free(&i
->compress
);
71 sd_event_source_unref(i
->input_event_source
);
73 safe_close(i
->output_fd
);
84 const char *image_root
,
85 RawImportFinished on_finished
,
88 _cleanup_(raw_import_unrefp
) RawImport
*i
= NULL
;
89 _cleanup_free_
char *root
= NULL
;
94 root
= strdup(image_root
?: "/var/lib/machines");
98 i
= new(RawImport
, 1);
105 .on_finished
= on_finished
,
106 .userdata
= userdata
,
107 .last_percent
= (unsigned) -1,
108 .image_root
= TAKE_PTR(root
),
109 .progress_ratelimit
= { 100 * USEC_PER_MSEC
, 1 },
113 i
->event
= sd_event_ref(event
);
115 r
= sd_event_default(&i
->event
);
125 static void raw_import_report_progress(RawImport
*i
) {
129 /* We have no size information, unless the source is a regular file */
130 if (!S_ISREG(i
->st
.st_mode
))
133 if (i
->written_compressed
>= (uint64_t) i
->st
.st_size
)
136 percent
= (unsigned) ((i
->written_compressed
* UINT64_C(100)) / (uint64_t) i
->st
.st_size
);
138 if (percent
== i
->last_percent
)
141 if (!ratelimit_below(&i
->progress_ratelimit
))
144 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
145 log_info("Imported %u%%.", percent
);
147 i
->last_percent
= percent
;
150 static int raw_import_maybe_convert_qcow2(RawImport
*i
) {
151 _cleanup_close_
int converted_fd
= -1;
152 _cleanup_free_
char *t
= NULL
;
157 r
= qcow2_detect(i
->output_fd
);
159 return log_error_errno(r
, "Failed to detect whether this is a QCOW2 image: %m");
163 /* This is a QCOW2 image, let's convert it */
164 r
= tempfn_random(i
->final_path
, NULL
, &t
);
168 converted_fd
= open(t
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
169 if (converted_fd
< 0)
170 return log_error_errno(errno
, "Failed to create %s: %m", t
);
172 (void) import_set_nocow_and_log(converted_fd
, t
);
174 log_info("Unpacking QCOW2 file.");
176 r
= qcow2_convert(i
->output_fd
, converted_fd
);
179 return log_error_errno(r
, "Failed to convert qcow2 image: %m");
182 (void) unlink(i
->temp_path
);
183 free_and_replace(i
->temp_path
, t
);
184 CLOSE_AND_REPLACE(i
->output_fd
, converted_fd
);
189 static int raw_import_finish(RawImport
*i
) {
193 assert(i
->output_fd
>= 0);
194 assert(i
->temp_path
);
195 assert(i
->final_path
);
197 /* In case this was a sparse file, make sure the file system is right */
198 if (i
->written_uncompressed
> 0) {
199 if (ftruncate(i
->output_fd
, i
->written_uncompressed
) < 0)
200 return log_error_errno(errno
, "Failed to truncate file: %m");
203 r
= raw_import_maybe_convert_qcow2(i
);
207 if (S_ISREG(i
->st
.st_mode
)) {
208 (void) copy_times(i
->input_fd
, i
->output_fd
, COPY_CRTIME
);
209 (void) copy_xattr(i
->input_fd
, i
->output_fd
);
212 if (i
->flags
& IMPORT_READ_ONLY
) {
213 r
= import_make_read_only_fd(i
->output_fd
);
218 if (i
->flags
& IMPORT_FORCE
)
219 (void) rm_rf(i
->final_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
221 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
223 return log_error_errno(r
, "Failed to move image into place: %m");
225 i
->temp_path
= mfree(i
->temp_path
);
230 static int raw_import_open_disk(RawImport
*i
) {
235 assert(!i
->final_path
);
236 assert(!i
->temp_path
);
237 assert(i
->output_fd
< 0);
239 i
->final_path
= strjoin(i
->image_root
, "/", i
->local
, ".raw");
243 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
247 (void) mkdir_parents_label(i
->temp_path
, 0700);
249 i
->output_fd
= open(i
->temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
250 if (i
->output_fd
< 0)
251 return log_error_errno(errno
, "Failed to open destination %s: %m", i
->temp_path
);
253 (void) import_set_nocow_and_log(i
->output_fd
, i
->temp_path
);
257 static int raw_import_try_reflink(RawImport
*i
) {
262 assert(i
->input_fd
>= 0);
263 assert(i
->output_fd
>= 0);
265 if (i
->compress
.type
!= IMPORT_COMPRESS_UNCOMPRESSED
)
268 if (!S_ISREG(i
->st
.st_mode
))
271 p
= lseek(i
->input_fd
, 0, SEEK_CUR
);
273 return log_error_errno(errno
, "Failed to read file offset of input file: %m");
275 /* Let's only try a btrfs reflink, if we are reading from the beginning of the file */
276 if ((uint64_t) p
!= (uint64_t) i
->buffer_size
)
279 r
= btrfs_reflink(i
->input_fd
, i
->output_fd
);
286 static int raw_import_write(const void *p
, size_t sz
, void *userdata
) {
287 RawImport
*i
= userdata
;
290 n
= sparse_write(i
->output_fd
, p
, sz
, 64);
296 i
->written_uncompressed
+= sz
;
301 static int raw_import_process(RawImport
*i
) {
306 assert(i
->buffer_size
< sizeof(i
->buffer
));
308 l
= read(i
->input_fd
, i
->buffer
+ i
->buffer_size
, sizeof(i
->buffer
) - i
->buffer_size
);
313 r
= log_error_errno(errno
, "Failed to read input file: %m");
319 if (i
->compress
.type
== IMPORT_COMPRESS_UNKNOWN
) {
321 if (l
== 0) { /* EOF */
322 log_debug("File too short to be compressed, as no compression signature fits in, thus assuming uncompressed.");
323 import_uncompress_force_off(&i
->compress
);
325 r
= import_uncompress_detect(&i
->compress
, i
->buffer
, i
->buffer_size
);
327 log_error_errno(r
, "Failed to detect file compression: %m");
330 if (r
== 0) /* Need more data */
334 r
= raw_import_open_disk(i
);
338 r
= raw_import_try_reflink(i
);
345 r
= import_uncompress(&i
->compress
, i
->buffer
, i
->buffer_size
, raw_import_write
, i
);
347 log_error_errno(r
, "Failed to decode and write: %m");
351 i
->written_compressed
+= i
->buffer_size
;
354 if (l
== 0) /* EOF */
357 raw_import_report_progress(i
);
362 r
= raw_import_finish(i
);
366 i
->on_finished(i
, r
, i
->userdata
);
368 sd_event_exit(i
->event
, r
);
373 static int raw_import_on_input(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
374 RawImport
*i
= userdata
;
376 return raw_import_process(i
);
379 static int raw_import_on_defer(sd_event_source
*s
, void *userdata
) {
380 RawImport
*i
= userdata
;
382 return raw_import_process(i
);
385 int raw_import_start(RawImport
*i
, int fd
, const char *local
, ImportFlags flags
) {
391 assert(!(flags
& ~IMPORT_FLAGS_MASK
));
393 if (!hostname_is_valid(local
, 0))
396 if (i
->input_fd
>= 0)
399 r
= fd_nonblock(fd
, true);
403 r
= free_and_strdup(&i
->local
, local
);
409 if (fstat(fd
, &i
->st
) < 0)
412 r
= sd_event_add_io(i
->event
, &i
->input_event_source
, fd
, EPOLLIN
, raw_import_on_input
, i
);
414 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
415 r
= sd_event_add_defer(i
->event
, &i
->input_event_source
, raw_import_on_defer
, i
);
419 r
= sd_event_source_set_enabled(i
->input_event_source
, SD_EVENT_ON
);