1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <sys/sendfile.h>
8 #include "alloc-util.h"
10 #include "export-raw.h"
12 #include "format-util.h"
15 #include "pretty-print.h"
16 #include "ratelimit.h"
17 #include "stat-util.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 RawExport
{
28 RawExportFinished on_finished
;
36 ImportCompress compress
;
38 sd_event_source
*output_event_source
;
42 size_t buffer_allocated
;
44 uint64_t written_compressed
;
45 uint64_t written_uncompressed
;
47 unsigned last_percent
;
48 RateLimit progress_ratelimit
;
57 RawExport
*raw_export_unref(RawExport
*e
) {
61 sd_event_source_unref(e
->output_event_source
);
63 import_compress_free(&e
->compress
);
65 sd_event_unref(e
->event
);
67 safe_close(e
->input_fd
);
77 RawExportFinished on_finished
,
80 _cleanup_(raw_export_unrefp
) RawExport
*e
= NULL
;
85 e
= new(RawExport
, 1);
92 .on_finished
= on_finished
,
94 .last_percent
= UINT_MAX
,
95 .progress_ratelimit
= { 100 * USEC_PER_MSEC
, 1 },
99 e
->event
= sd_event_ref(event
);
101 r
= sd_event_default(&e
->event
);
111 static void raw_export_report_progress(RawExport
*e
) {
115 if (e
->written_uncompressed
>= (uint64_t) e
->st
.st_size
)
118 percent
= (unsigned) ((e
->written_uncompressed
* UINT64_C(100)) / (uint64_t) e
->st
.st_size
);
120 if (percent
== e
->last_percent
)
123 if (!ratelimit_below(&e
->progress_ratelimit
))
126 sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent
);
128 if (isatty_safe(STDERR_FILENO
))
129 (void) draw_progress_barf(
132 glyph(GLYPH_ARROW_RIGHT
),
133 FORMAT_BYTES(e
->written_uncompressed
),
134 FORMAT_BYTES(e
->st
.st_size
));
136 log_info("Exported %u%%.", percent
);
138 e
->last_percent
= percent
;
141 static int raw_export_process(RawExport
*e
) {
147 if (!e
->tried_reflink
&& e
->compress
.type
== IMPORT_COMPRESS_UNCOMPRESSED
) {
149 /* If we shall take an uncompressed snapshot we can
150 * reflink source to destination directly. Let's see
153 r
= reflink(e
->input_fd
, e
->output_fd
);
159 e
->tried_reflink
= true;
162 if (!e
->tried_sendfile
&& e
->compress
.type
== IMPORT_COMPRESS_UNCOMPRESSED
) {
164 l
= sendfile(e
->output_fd
, e
->input_fd
, NULL
, COPY_BUFFER_SIZE
);
169 e
->tried_sendfile
= true;
174 e
->written_uncompressed
+= l
;
175 e
->written_compressed
+= l
;
177 raw_export_report_progress(e
);
183 while (e
->buffer_size
<= 0) {
184 uint8_t input
[COPY_BUFFER_SIZE
];
191 l
= read(e
->input_fd
, input
, sizeof(input
));
193 r
= log_error_errno(errno
, "Failed to read raw file: %m");
199 r
= import_compress_finish(&e
->compress
, &e
->buffer
, &e
->buffer_size
, &e
->buffer_allocated
);
201 e
->written_uncompressed
+= l
;
202 r
= import_compress(&e
->compress
, input
, l
, &e
->buffer
, &e
->buffer_size
, &e
->buffer_allocated
);
205 r
= log_error_errno(r
, "Failed to encode: %m");
210 l
= write(e
->output_fd
, e
->buffer
, e
->buffer_size
);
215 r
= log_error_errno(errno
, "Failed to write output file: %m");
219 assert((size_t) l
<= e
->buffer_size
);
220 memmove(e
->buffer
, (uint8_t*) e
->buffer
+ l
, e
->buffer_size
- l
);
222 e
->written_compressed
+= l
;
224 raw_export_report_progress(e
);
230 if (isatty_safe(STDERR_FILENO
))
231 clear_progress_bar(/* prefix= */ NULL
);
233 (void) copy_times(e
->input_fd
, e
->output_fd
, COPY_CRTIME
);
234 (void) copy_xattr(e
->input_fd
, NULL
, e
->output_fd
, NULL
, 0);
238 e
->on_finished(e
, r
, e
->userdata
);
240 sd_event_exit(e
->event
, r
);
245 static int raw_export_on_output(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
246 RawExport
*i
= userdata
;
248 return raw_export_process(i
);
251 static int raw_export_on_defer(sd_event_source
*s
, void *userdata
) {
252 RawExport
*i
= userdata
;
254 return raw_export_process(i
);
257 static int reflink_snapshot(int fd
, const char *path
) {
260 new_fd
= open_parent(path
, O_TMPFILE
|O_CLOEXEC
|O_RDWR
, 0600);
262 _cleanup_free_
char *t
= NULL
;
264 r
= tempfn_random(path
, NULL
, &t
);
268 new_fd
= open(t
, O_CLOEXEC
|O_CREAT
|O_NOCTTY
|O_RDWR
, 0600);
275 r
= reflink(fd
, new_fd
);
284 int raw_export_start(RawExport
*e
, const char *path
, int fd
, ImportCompressType compress
) {
285 _cleanup_close_
int sfd
= -EBADF
, tfd
= -EBADF
;
291 assert(compress
< _IMPORT_COMPRESS_TYPE_MAX
);
292 assert(compress
!= IMPORT_COMPRESS_UNKNOWN
);
294 if (e
->output_fd
>= 0)
297 r
= fd_nonblock(fd
, true);
301 r
= free_and_strdup(&e
->path
, path
);
305 sfd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
309 if (fstat(sfd
, &e
->st
) < 0)
311 r
= stat_verify_regular(&e
->st
);
315 /* Try to take a reflink snapshot of the file, if we can t make the export atomic */
316 tfd
= reflink_snapshot(sfd
, path
);
318 e
->input_fd
= TAKE_FD(tfd
);
320 e
->input_fd
= TAKE_FD(sfd
);
322 r
= import_compress_init(&e
->compress
, compress
);
326 r
= sd_event_add_io(e
->event
, &e
->output_event_source
, fd
, EPOLLOUT
, raw_export_on_output
, e
);
328 r
= sd_event_add_defer(e
->event
, &e
->output_event_source
, raw_export_on_defer
, e
);
332 r
= sd_event_source_set_enabled(e
->output_event_source
, SD_EVENT_ON
);