1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "sd-daemon.h"
27 #include "alloc-util.h"
28 #include "btrfs-util.h"
33 #include "hostname-util.h"
34 #include "import-common.h"
35 #include "import-compress.h"
36 #include "import-tar.h"
38 #include "machine-pool.h"
40 #include "path-util.h"
41 #include "process-util.h"
42 #include "qcow2-util.h"
43 #include "ratelimit.h"
45 #include "string-util.h"
53 TarImportFinished on_finished
;
59 bool grow_machine_directory
;
67 ImportCompress compress
;
69 uint64_t written_since_last_grow
;
71 sd_event_source
*input_event_source
;
73 uint8_t buffer
[16*1024];
76 uint64_t written_compressed
;
77 uint64_t written_uncompressed
;
83 unsigned last_percent
;
84 RateLimit progress_rate_limit
;
87 TarImport
* tar_import_unref(TarImport
*i
) {
91 sd_event_source_unref(i
->input_event_source
);
94 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
95 (void) wait_for_terminate(i
->tar_pid
, NULL
);
99 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
103 import_compress_free(&i
->compress
);
105 sd_event_unref(i
->event
);
107 safe_close(i
->tar_fd
);
120 const char *image_root
,
121 TarImportFinished on_finished
,
124 _cleanup_(tar_import_unrefp
) TarImport
*i
= NULL
;
129 i
= new0(TarImport
, 1);
133 i
->input_fd
= i
->tar_fd
= -1;
134 i
->on_finished
= on_finished
;
135 i
->userdata
= userdata
;
137 RATELIMIT_INIT(i
->progress_rate_limit
, 100 * USEC_PER_MSEC
, 1);
138 i
->last_percent
= (unsigned) -1;
140 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
144 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
147 i
->event
= sd_event_ref(event
);
149 r
= sd_event_default(&i
->event
);
160 static void tar_import_report_progress(TarImport
*i
) {
164 /* We have no size information, unless the source is a regular file */
165 if (!S_ISREG(i
->st
.st_mode
))
168 if (i
->written_compressed
>= (uint64_t) i
->st
.st_size
)
171 percent
= (unsigned) ((i
->written_compressed
* UINT64_C(100)) / (uint64_t) i
->st
.st_size
);
173 if (percent
== i
->last_percent
)
176 if (!ratelimit_test(&i
->progress_rate_limit
))
179 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
180 log_info("Imported %u%%.", percent
);
182 i
->last_percent
= percent
;
185 static int tar_import_finish(TarImport
*i
) {
189 assert(i
->tar_fd
>= 0);
190 assert(i
->temp_path
);
191 assert(i
->final_path
);
193 i
->tar_fd
= safe_close(i
->tar_fd
);
195 if (i
->tar_pid
> 0) {
196 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
203 r
= import_make_read_only(i
->temp_path
);
209 (void) rm_rf(i
->final_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
211 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
213 return log_error_errno(r
, "Failed to move image into place: %m");
215 i
->temp_path
= mfree(i
->temp_path
);
220 static int tar_import_fork_tar(TarImport
*i
) {
225 assert(!i
->final_path
);
226 assert(!i
->temp_path
);
227 assert(i
->tar_fd
< 0);
229 i
->final_path
= strjoin(i
->image_root
, "/", i
->local
, NULL
);
233 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
237 (void) mkdir_parents_label(i
->temp_path
, 0700);
239 r
= btrfs_subvol_make(i
->temp_path
);
241 if (mkdir(i
->temp_path
, 0755) < 0)
242 return log_error_errno(errno
, "Failed to create directory %s: %m", i
->temp_path
);
244 return log_error_errno(errno
, "Failed to create subvolume %s: %m", i
->temp_path
);
246 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
248 i
->tar_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
255 static int tar_import_write(const void *p
, size_t sz
, void *userdata
) {
256 TarImport
*i
= userdata
;
259 if (i
->grow_machine_directory
&& i
->written_since_last_grow
>= GROW_INTERVAL_BYTES
) {
260 i
->written_since_last_grow
= 0;
261 grow_machine_directory();
264 r
= loop_write(i
->tar_fd
, p
, sz
, false);
268 i
->written_uncompressed
+= sz
;
269 i
->written_since_last_grow
+= sz
;
274 static int tar_import_process(TarImport
*i
) {
279 assert(i
->buffer_size
< sizeof(i
->buffer
));
281 l
= read(i
->input_fd
, i
->buffer
+ i
->buffer_size
, sizeof(i
->buffer
) - i
->buffer_size
);
286 r
= log_error_errno(errno
, "Failed to read input file: %m");
290 if (i
->compress
.type
== IMPORT_COMPRESS_UNKNOWN
) {
291 log_error("Premature end of file: %m");
296 r
= tar_import_finish(i
);
302 if (i
->compress
.type
== IMPORT_COMPRESS_UNKNOWN
) {
303 r
= import_uncompress_detect(&i
->compress
, i
->buffer
, i
->buffer_size
);
305 log_error("Failed to detect file compression: %m");
308 if (r
== 0) /* Need more data */
311 r
= tar_import_fork_tar(i
);
316 r
= import_uncompress(&i
->compress
, i
->buffer
, i
->buffer_size
, tar_import_write
, i
);
318 log_error_errno(r
, "Failed to decode and write: %m");
322 i
->written_compressed
+= i
->buffer_size
;
325 tar_import_report_progress(i
);
331 i
->on_finished(i
, r
, i
->userdata
);
333 sd_event_exit(i
->event
, r
);
338 static int tar_import_on_input(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
339 TarImport
*i
= userdata
;
341 return tar_import_process(i
);
344 static int tar_import_on_defer(sd_event_source
*s
, void *userdata
) {
345 TarImport
*i
= userdata
;
347 return tar_import_process(i
);
350 int tar_import_start(TarImport
*i
, int fd
, const char *local
, bool force_local
, bool read_only
) {
357 if (!machine_name_is_valid(local
))
360 if (i
->input_fd
>= 0)
363 r
= fd_nonblock(fd
, true);
367 r
= free_and_strdup(&i
->local
, local
);
370 i
->force_local
= force_local
;
371 i
->read_only
= read_only
;
373 if (fstat(fd
, &i
->st
) < 0)
376 r
= sd_event_add_io(i
->event
, &i
->input_event_source
, fd
, EPOLLIN
, tar_import_on_input
, i
);
378 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
379 r
= sd_event_add_defer(i
->event
, &i
->input_event_source
, tar_import_on_defer
, i
);
383 r
= sd_event_source_set_enabled(i
->input_event_source
, SD_EVENT_ON
);