1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "alloc-util.h"
9 #include "btrfs-util.h"
10 #include "export-tar.h"
12 #include "format-util.h"
13 #include "import-common.h"
15 #include "pretty-print.h"
16 #include "process-util.h"
17 #include "ratelimit.h"
18 #include "string-util.h"
19 #include "terminal-util.h"
20 #include "time-util.h"
21 #include "tmpfile-util.h"
23 #define COPY_BUFFER_SIZE (16*1024)
25 typedef struct TarExport
{
28 TarExportFinished on_finished
;
37 ImportCompress compress
;
39 sd_event_source
*output_event_source
;
43 size_t buffer_allocated
;
45 uint64_t written_compressed
;
46 uint64_t written_uncompressed
;
51 uint64_t quota_referenced
;
53 unsigned last_percent
;
54 RateLimit progress_ratelimit
;
60 TarExport
*tar_export_unref(TarExport
*e
) {
64 sd_event_source_unref(e
->output_event_source
);
67 sigkill_wait(e
->tar_pid
);
70 (void) btrfs_subvol_remove(e
->temp_path
, BTRFS_REMOVE_QUOTA
);
74 import_compress_free(&e
->compress
);
76 sd_event_unref(e
->event
);
78 safe_close(e
->tar_fd
);
88 TarExportFinished on_finished
,
91 _cleanup_(tar_export_unrefp
) TarExport
*e
= NULL
;
96 e
= new(TarExport
, 1);
103 .on_finished
= on_finished
,
104 .userdata
= userdata
,
105 .quota_referenced
= UINT64_MAX
,
106 .last_percent
= UINT_MAX
,
107 .progress_ratelimit
= { 100 * USEC_PER_MSEC
, 1 },
111 e
->event
= sd_event_ref(event
);
113 r
= sd_event_default(&e
->event
);
123 static void tar_export_report_progress(TarExport
*e
) {
127 /* Do we have any quota info? If not, we don't know anything about the progress */
128 if (e
->quota_referenced
== UINT64_MAX
)
131 if (e
->written_uncompressed
>= e
->quota_referenced
)
134 percent
= (unsigned) ((e
->written_uncompressed
* UINT64_C(100)) / e
->quota_referenced
);
136 if (percent
== e
->last_percent
)
139 if (!ratelimit_below(&e
->progress_ratelimit
))
142 sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent
);
144 if (isatty_safe(STDERR_FILENO
))
145 (void) draw_progress_barf(
148 glyph(GLYPH_ARROW_RIGHT
),
149 FORMAT_BYTES(e
->written_uncompressed
),
150 FORMAT_BYTES(e
->quota_referenced
));
152 log_info("Exported %u%%.", percent
);
154 e
->last_percent
= percent
;
157 static int tar_export_finish(TarExport
*e
) {
161 assert(e
->tar_fd
>= 0);
163 if (e
->tar_pid
> 0) {
164 r
= wait_for_terminate_and_check("tar", TAKE_PID(e
->tar_pid
), WAIT_LOG
);
167 if (r
!= EXIT_SUCCESS
)
171 e
->tar_fd
= safe_close(e
->tar_fd
);
176 static int tar_export_process(TarExport
*e
) {
182 if (!e
->tried_splice
&& e
->compress
.type
== IMPORT_COMPRESS_UNCOMPRESSED
) {
184 l
= splice(e
->tar_fd
, NULL
, e
->output_fd
, NULL
, COPY_BUFFER_SIZE
, 0);
189 e
->tried_splice
= true;
191 r
= tar_export_finish(e
);
194 e
->written_uncompressed
+= l
;
195 e
->written_compressed
+= l
;
197 tar_export_report_progress(e
);
203 while (e
->buffer_size
<= 0) {
204 uint8_t input
[COPY_BUFFER_SIZE
];
207 r
= tar_export_finish(e
);
211 l
= read(e
->tar_fd
, input
, sizeof(input
));
213 r
= log_error_errno(errno
, "Failed to read tar file: %m");
219 r
= import_compress_finish(&e
->compress
, &e
->buffer
, &e
->buffer_size
, &e
->buffer_allocated
);
221 e
->written_uncompressed
+= l
;
222 r
= import_compress(&e
->compress
, input
, l
, &e
->buffer
, &e
->buffer_size
, &e
->buffer_allocated
);
225 r
= log_error_errno(r
, "Failed to encode: %m");
230 l
= write(e
->output_fd
, e
->buffer
, e
->buffer_size
);
235 r
= log_error_errno(errno
, "Failed to write output file: %m");
239 assert((size_t) l
<= e
->buffer_size
);
240 memmove(e
->buffer
, (uint8_t*) e
->buffer
+ l
, e
->buffer_size
- l
);
242 e
->written_compressed
+= l
;
244 tar_export_report_progress(e
);
249 if (r
>= 0 && isatty_safe(STDERR_FILENO
))
250 clear_progress_bar(/* prefix= */ NULL
);
253 e
->on_finished(e
, r
, e
->userdata
);
255 sd_event_exit(e
->event
, r
);
260 static int tar_export_on_output(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
261 TarExport
*i
= userdata
;
263 return tar_export_process(i
);
266 static int tar_export_on_defer(sd_event_source
*s
, void *userdata
) {
267 TarExport
*i
= userdata
;
269 return tar_export_process(i
);
272 int tar_export_start(TarExport
*e
, const char *path
, int fd
, ImportCompressType compress
) {
273 _cleanup_close_
int sfd
= -EBADF
;
279 assert(compress
< _IMPORT_COMPRESS_TYPE_MAX
);
280 assert(compress
!= IMPORT_COMPRESS_UNKNOWN
);
282 if (e
->output_fd
>= 0)
285 sfd
= open(path
, O_DIRECTORY
|O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
289 if (fstat(sfd
, &e
->st
) < 0)
292 r
= fd_nonblock(fd
, true);
296 r
= free_and_strdup(&e
->path
, path
);
300 e
->quota_referenced
= UINT64_MAX
;
302 if (btrfs_might_be_subvol(&e
->st
)) {
305 r
= btrfs_subvol_get_subtree_quota_fd(sfd
, 0, &q
);
307 e
->quota_referenced
= q
.referenced
;
309 e
->temp_path
= mfree(e
->temp_path
);
311 r
= tempfn_random(path
, NULL
, &e
->temp_path
);
315 /* Let's try to make a snapshot, if we can, so that the export is atomic */
316 r
= btrfs_subvol_snapshot_at(sfd
, NULL
, AT_FDCWD
, e
->temp_path
, BTRFS_SNAPSHOT_READ_ONLY
|BTRFS_SNAPSHOT_RECURSIVE
);
318 log_debug_errno(r
, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e
->temp_path
, path
);
319 e
->temp_path
= mfree(e
->temp_path
);
323 r
= import_compress_init(&e
->compress
, compress
);
327 r
= sd_event_add_io(e
->event
, &e
->output_event_source
, fd
, EPOLLOUT
, tar_export_on_output
, e
);
329 r
= sd_event_add_defer(e
->event
, &e
->output_event_source
, tar_export_on_defer
, e
);
333 r
= sd_event_source_set_enabled(e
->output_event_source
, SD_EVENT_ON
);
338 e
->tar_fd
= import_fork_tar_c(e
->temp_path
?: e
->path
, &e
->tar_pid
);
340 e
->output_event_source
= sd_event_source_unref(e
->output_event_source
);