1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "alloc-util.h"
9 #include "btrfs-util.h"
10 #include "errno-util.h"
12 #include "format-util.h"
13 #include "import-common.h"
14 #include "import-compress.h"
15 #include "import-tar.h"
16 #include "import-util.h"
17 #include "install-file.h"
20 #include "mkdir-label.h"
21 #include "path-util.h"
22 #include "pretty-print.h"
23 #include "process-util.h"
24 #include "ratelimit.h"
26 #include "string-util.h"
27 #include "terminal-util.h"
28 #include "time-util.h"
29 #include "tmpfile-util.h"
31 typedef struct TarImport
{
36 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
;
58 struct stat input_stat
;
62 unsigned last_percent
;
63 RateLimit progress_ratelimit
;
66 TarImport
* tar_import_unref(TarImport
*i
) {
70 sd_event_source_unref(i
->input_event_source
);
73 sigkill_wait(i
->tar_pid
);
75 rm_rf_subvolume_and_free(i
->temp_path
);
77 import_compress_free(&i
->compress
);
79 sd_event_unref(i
->event
);
81 safe_close(i
->tar_fd
);
92 const char *image_root
,
93 TarImportFinished on_finished
,
96 _cleanup_(tar_import_unrefp
) TarImport
*i
= NULL
;
97 _cleanup_free_
char *root
= NULL
;
103 root
= strdup(image_root
);
107 i
= new(TarImport
, 1);
114 .on_finished
= on_finished
,
115 .userdata
= userdata
,
116 .last_percent
= UINT_MAX
,
117 .image_root
= TAKE_PTR(root
),
118 .progress_ratelimit
= { 100 * USEC_PER_MSEC
, 1 },
122 i
->event
= sd_event_ref(event
);
124 r
= sd_event_default(&i
->event
);
134 static void tar_import_report_progress(TarImport
*i
) {
138 /* We have no size information, unless the source is a regular file */
139 if (!S_ISREG(i
->input_stat
.st_mode
))
142 if (i
->written_compressed
>= (uint64_t) i
->input_stat
.st_size
)
145 percent
= (unsigned) ((i
->written_compressed
* UINT64_C(100)) / (uint64_t) i
->input_stat
.st_size
);
147 if (percent
== i
->last_percent
)
150 if (!ratelimit_below(&i
->progress_ratelimit
))
153 sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent
);
155 if (isatty_safe(STDERR_FILENO
))
156 (void) draw_progress_barf(
159 glyph(GLYPH_ARROW_RIGHT
),
160 FORMAT_BYTES(i
->written_compressed
),
161 FORMAT_BYTES(i
->input_stat
.st_size
));
163 log_info("Imported %u%%.", percent
);
165 i
->last_percent
= percent
;
168 static int tar_import_finish(TarImport
*i
) {
173 assert(i
->tar_fd
>= 0);
175 i
->tar_fd
= safe_close(i
->tar_fd
);
177 if (i
->tar_pid
> 0) {
178 r
= wait_for_terminate_and_check("tar", TAKE_PID(i
->tar_pid
), WAIT_LOG
);
181 if (r
!= EXIT_SUCCESS
)
185 assert_se(d
= i
->temp_path
?: i
->local
);
187 r
= import_mangle_os_tree(d
);
193 AT_FDCWD
, i
->final_path
,
194 (i
->flags
& IMPORT_FORCE
? INSTALL_REPLACE
: 0) |
195 (i
->flags
& IMPORT_READ_ONLY
? INSTALL_READ_ONLY
: 0) |
196 (i
->flags
& IMPORT_SYNC
? INSTALL_SYNCFS
: 0));
198 return log_error_errno(r
, "Failed to move '%s' into place: %m", i
->final_path
?: i
->local
);
200 i
->temp_path
= mfree(i
->temp_path
);
205 static int tar_import_fork_tar(TarImport
*i
) {
206 const char *d
, *root
;
211 assert(!i
->final_path
);
212 assert(!i
->temp_path
);
213 assert(i
->tar_fd
< 0);
215 if (i
->flags
& IMPORT_DIRECT
) {
219 i
->final_path
= path_join(i
->image_root
, i
->local
);
223 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
228 root
= i
->image_root
;
233 (void) mkdir_parents_label(d
, 0700);
235 if (FLAGS_SET(i
->flags
, IMPORT_DIRECT
|IMPORT_FORCE
))
236 (void) rm_rf(d
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
238 if (i
->flags
& IMPORT_BTRFS_SUBVOL
)
239 r
= btrfs_subvol_make_fallback(AT_FDCWD
, d
, 0755);
241 r
= RET_NERRNO(mkdir(d
, 0755));
242 if (r
== -EEXIST
&& (i
->flags
& IMPORT_DIRECT
)) /* EEXIST is OK if in direct mode, but not otherwise,
243 * because in that case our temporary path collided */
246 return log_error_errno(r
, "Failed to create directory/subvolume %s: %m", d
);
247 if (r
> 0 && (i
->flags
& IMPORT_BTRFS_QUOTA
)) { /* actually btrfs subvol */
248 if (!(i
->flags
& IMPORT_DIRECT
))
249 (void) import_assign_pool_quota_and_warn(root
);
250 (void) import_assign_pool_quota_and_warn(d
);
253 i
->tar_fd
= import_fork_tar_x(d
, &i
->tar_pid
);
260 static int tar_import_write(const void *p
, size_t sz
, void *userdata
) {
261 TarImport
*i
= userdata
;
264 r
= loop_write(i
->tar_fd
, p
, sz
);
268 i
->written_uncompressed
+= sz
;
273 static int tar_import_process(TarImport
*i
) {
278 assert(i
->buffer_size
< sizeof(i
->buffer
));
280 l
= read(i
->input_fd
, i
->buffer
+ i
->buffer_size
, sizeof(i
->buffer
) - i
->buffer_size
);
285 r
= log_error_errno(errno
, "Failed to read input file: %m");
289 if ((size_t) l
> sizeof(i
->buffer
) - i
->buffer_size
) {
290 r
= log_error_errno(SYNTHETIC_ERRNO(EBADMSG
), "Read input file exceeded maximum size.");
296 if (i
->compress
.type
== IMPORT_COMPRESS_UNKNOWN
) {
298 if (l
== 0) { /* EOF */
299 log_debug("File too short to be compressed, as no compression signature fits in, thus assuming uncompressed.");
300 import_uncompress_force_off(&i
->compress
);
302 r
= import_uncompress_detect(&i
->compress
, i
->buffer
, i
->buffer_size
);
304 log_error_errno(r
, "Failed to detect file compression: %m");
307 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 if (l
== 0) { /* EOF */
326 r
= tar_import_finish(i
);
330 tar_import_report_progress(i
);
335 if (r
>= 0 && isatty_safe(STDERR_FILENO
))
336 clear_progress_bar(/* prefix= */ NULL
);
339 i
->on_finished(i
, r
, i
->userdata
);
341 sd_event_exit(i
->event
, r
);
346 static int tar_import_on_input(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
347 TarImport
*i
= userdata
;
349 return tar_import_process(i
);
352 static int tar_import_on_defer(sd_event_source
*s
, void *userdata
) {
353 TarImport
*i
= userdata
;
355 return tar_import_process(i
);
358 int tar_import_start(TarImport
*i
, int fd
, const char *local
, ImportFlags flags
) {
364 assert(!(flags
& ~IMPORT_FLAGS_MASK_TAR
));
366 if (!import_validate_local(local
, flags
))
369 if (i
->input_fd
>= 0)
372 r
= fd_nonblock(fd
, true);
376 r
= free_and_strdup(&i
->local
, local
);
382 if (fstat(fd
, &i
->input_stat
) < 0)
385 r
= sd_event_add_io(i
->event
, &i
->input_event_source
, fd
, EPOLLIN
, tar_import_on_input
, i
);
387 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
388 r
= sd_event_add_defer(i
->event
, &i
->input_event_source
, tar_import_on_defer
, i
);
392 r
= sd_event_source_set_enabled(i
->input_event_source
, SD_EVENT_ON
);