1 /* SPDX-License-Identifier: LGPL-2.1+ */
8 #include "alloc-util.h"
9 #include "btrfs-util.h"
14 #include "hostname-util.h"
15 #include "import-common.h"
16 #include "import-compress.h"
17 #include "import-tar.h"
19 #include "machine-pool.h"
21 #include "path-util.h"
22 #include "process-util.h"
23 #include "qcow2-util.h"
24 #include "ratelimit.h"
26 #include "string-util.h"
27 #include "tmpfile-util.h"
35 TarImportFinished on_finished
;
48 ImportCompress compress
;
50 sd_event_source
*input_event_source
;
52 uint8_t buffer
[16*1024];
55 uint64_t written_compressed
;
56 uint64_t written_uncompressed
;
62 unsigned last_percent
;
63 RateLimit progress_rate_limit
;
66 TarImport
* tar_import_unref(TarImport
*i
) {
70 sd_event_source_unref(i
->input_event_source
);
73 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
74 (void) wait_for_terminate(i
->tar_pid
, NULL
);
78 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
82 import_compress_free(&i
->compress
);
84 sd_event_unref(i
->event
);
86 safe_close(i
->tar_fd
);
97 const char *image_root
,
98 TarImportFinished on_finished
,
101 _cleanup_(tar_import_unrefp
) TarImport
*i
= NULL
;
102 _cleanup_free_
char *root
= NULL
;
107 root
= strdup(image_root
?: "/var/lib/machines");
111 i
= new(TarImport
, 1);
118 .on_finished
= on_finished
,
119 .userdata
= userdata
,
120 .last_percent
= (unsigned) -1,
121 .image_root
= TAKE_PTR(root
),
124 RATELIMIT_INIT(i
->progress_rate_limit
, 100 * USEC_PER_MSEC
, 1);
127 i
->event
= sd_event_ref(event
);
129 r
= sd_event_default(&i
->event
);
139 static void tar_import_report_progress(TarImport
*i
) {
143 /* We have no size information, unless the source is a regular file */
144 if (!S_ISREG(i
->st
.st_mode
))
147 if (i
->written_compressed
>= (uint64_t) i
->st
.st_size
)
150 percent
= (unsigned) ((i
->written_compressed
* UINT64_C(100)) / (uint64_t) i
->st
.st_size
);
152 if (percent
== i
->last_percent
)
155 if (!ratelimit_below(&i
->progress_rate_limit
))
158 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
159 log_info("Imported %u%%.", percent
);
161 i
->last_percent
= percent
;
164 static int tar_import_finish(TarImport
*i
) {
168 assert(i
->tar_fd
>= 0);
169 assert(i
->temp_path
);
170 assert(i
->final_path
);
172 i
->tar_fd
= safe_close(i
->tar_fd
);
174 if (i
->tar_pid
> 0) {
175 r
= wait_for_terminate_and_check("tar", i
->tar_pid
, WAIT_LOG
);
179 if (r
!= EXIT_SUCCESS
)
183 r
= import_mangle_os_tree(i
->temp_path
);
188 r
= import_make_read_only(i
->temp_path
);
194 (void) rm_rf(i
->final_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
196 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
198 return log_error_errno(r
, "Failed to move image into place: %m");
200 i
->temp_path
= mfree(i
->temp_path
);
205 static int tar_import_fork_tar(TarImport
*i
) {
210 assert(!i
->final_path
);
211 assert(!i
->temp_path
);
212 assert(i
->tar_fd
< 0);
214 i
->final_path
= strjoin(i
->image_root
, "/", i
->local
);
218 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
222 (void) mkdir_parents_label(i
->temp_path
, 0700);
224 r
= btrfs_subvol_make(i
->temp_path
);
226 if (mkdir(i
->temp_path
, 0755) < 0)
227 return log_error_errno(errno
, "Failed to create directory %s: %m", i
->temp_path
);
229 return log_error_errno(r
, "Failed to create subvolume %s: %m", i
->temp_path
);
231 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
233 i
->tar_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
240 static int tar_import_write(const void *p
, size_t sz
, void *userdata
) {
241 TarImport
*i
= userdata
;
244 r
= loop_write(i
->tar_fd
, p
, sz
, false);
248 i
->written_uncompressed
+= sz
;
253 static int tar_import_process(TarImport
*i
) {
258 assert(i
->buffer_size
< sizeof(i
->buffer
));
260 l
= read(i
->input_fd
, i
->buffer
+ i
->buffer_size
, sizeof(i
->buffer
) - i
->buffer_size
);
265 r
= log_error_errno(errno
, "Failed to read input file: %m");
269 if (i
->compress
.type
== IMPORT_COMPRESS_UNKNOWN
) {
270 log_error("Premature end of file.");
275 r
= tar_import_finish(i
);
281 if (i
->compress
.type
== IMPORT_COMPRESS_UNKNOWN
) {
282 r
= import_uncompress_detect(&i
->compress
, i
->buffer
, i
->buffer_size
);
284 log_error_errno(r
, "Failed to detect file compression: %m");
287 if (r
== 0) /* Need more data */
290 r
= tar_import_fork_tar(i
);
295 r
= import_uncompress(&i
->compress
, i
->buffer
, i
->buffer_size
, tar_import_write
, i
);
297 log_error_errno(r
, "Failed to decode and write: %m");
301 i
->written_compressed
+= i
->buffer_size
;
304 tar_import_report_progress(i
);
310 i
->on_finished(i
, r
, i
->userdata
);
312 sd_event_exit(i
->event
, r
);
317 static int tar_import_on_input(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
318 TarImport
*i
= userdata
;
320 return tar_import_process(i
);
323 static int tar_import_on_defer(sd_event_source
*s
, void *userdata
) {
324 TarImport
*i
= userdata
;
326 return tar_import_process(i
);
329 int tar_import_start(TarImport
*i
, int fd
, const char *local
, bool force_local
, bool read_only
) {
336 if (!machine_name_is_valid(local
))
339 if (i
->input_fd
>= 0)
342 r
= fd_nonblock(fd
, true);
346 r
= free_and_strdup(&i
->local
, local
);
349 i
->force_local
= force_local
;
350 i
->read_only
= read_only
;
352 if (fstat(fd
, &i
->st
) < 0)
355 r
= sd_event_add_io(i
->event
, &i
->input_event_source
, fd
, EPOLLIN
, tar_import_on_input
, i
);
357 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
358 r
= sd_event_add_defer(i
->event
, &i
->input_event_source
, tar_import_on_defer
, i
);
362 r
= sd_event_source_set_enabled(i
->input_event_source
, SD_EVENT_ON
);