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/>.
22 #include <sys/sendfile.h>
26 #include "sd-daemon.h"
28 #include "btrfs-util.h"
30 #include "export-raw.h"
33 #include "import-common.h"
34 #include "ratelimit.h"
35 #include "string-util.h"
38 #define COPY_BUFFER_SIZE (16*1024)
43 RawExportFinished on_finished
;
51 ImportCompress compress
;
53 sd_event_source
*output_event_source
;
57 size_t buffer_allocated
;
59 uint64_t written_compressed
;
60 uint64_t written_uncompressed
;
62 unsigned last_percent
;
63 RateLimit progress_rate_limit
;
72 RawExport
*raw_export_unref(RawExport
*e
) {
76 sd_event_source_unref(e
->output_event_source
);
78 import_compress_free(&e
->compress
);
80 sd_event_unref(e
->event
);
82 safe_close(e
->input_fd
);
94 RawExportFinished on_finished
,
97 _cleanup_(raw_export_unrefp
) RawExport
*e
= NULL
;
102 e
= new0(RawExport
, 1);
106 e
->output_fd
= e
->input_fd
= -1;
107 e
->on_finished
= on_finished
;
108 e
->userdata
= userdata
;
110 RATELIMIT_INIT(e
->progress_rate_limit
, 100 * USEC_PER_MSEC
, 1);
111 e
->last_percent
= (unsigned) -1;
114 e
->event
= sd_event_ref(event
);
116 r
= sd_event_default(&e
->event
);
127 static void raw_export_report_progress(RawExport
*e
) {
131 if (e
->written_uncompressed
>= (uint64_t) e
->st
.st_size
)
134 percent
= (unsigned) ((e
->written_uncompressed
* UINT64_C(100)) / (uint64_t) e
->st
.st_size
);
136 if (percent
== e
->last_percent
)
139 if (!ratelimit_test(&e
->progress_rate_limit
))
142 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
143 log_info("Exported %u%%.", percent
);
145 e
->last_percent
= percent
;
148 static int raw_export_process(RawExport
*e
) {
154 if (!e
->tried_reflink
&& e
->compress
.type
== IMPORT_COMPRESS_UNCOMPRESSED
) {
156 /* If we shall take an uncompressed snapshot we can
157 * reflink source to destination directly. Let's see
160 r
= btrfs_reflink(e
->input_fd
, e
->output_fd
);
166 e
->tried_reflink
= true;
169 if (!e
->tried_sendfile
&& e
->compress
.type
== IMPORT_COMPRESS_UNCOMPRESSED
) {
171 l
= sendfile(e
->output_fd
, e
->input_fd
, NULL
, COPY_BUFFER_SIZE
);
176 e
->tried_sendfile
= true;
181 e
->written_uncompressed
+= l
;
182 e
->written_compressed
+= l
;
184 raw_export_report_progress(e
);
190 while (e
->buffer_size
<= 0) {
191 uint8_t input
[COPY_BUFFER_SIZE
];
198 l
= read(e
->input_fd
, input
, sizeof(input
));
200 r
= log_error_errno(errno
, "Failed to read raw file: %m");
206 r
= import_compress_finish(&e
->compress
, &e
->buffer
, &e
->buffer_size
, &e
->buffer_allocated
);
208 e
->written_uncompressed
+= l
;
209 r
= import_compress(&e
->compress
, input
, l
, &e
->buffer
, &e
->buffer_size
, &e
->buffer_allocated
);
212 r
= log_error_errno(r
, "Failed to encode: %m");
217 l
= write(e
->output_fd
, e
->buffer
, e
->buffer_size
);
222 r
= log_error_errno(errno
, "Failed to write output file: %m");
226 assert((size_t) l
<= e
->buffer_size
);
227 memmove(e
->buffer
, (uint8_t*) e
->buffer
+ l
, e
->buffer_size
- l
);
229 e
->written_compressed
+= l
;
231 raw_export_report_progress(e
);
237 (void) copy_times(e
->input_fd
, e
->output_fd
);
238 (void) copy_xattr(e
->input_fd
, e
->output_fd
);
242 e
->on_finished(e
, r
, e
->userdata
);
244 sd_event_exit(e
->event
, r
);
249 static int raw_export_on_output(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
250 RawExport
*i
= userdata
;
252 return raw_export_process(i
);
255 static int raw_export_on_defer(sd_event_source
*s
, void *userdata
) {
256 RawExport
*i
= userdata
;
258 return raw_export_process(i
);
261 static int reflink_snapshot(int fd
, const char *path
) {
268 new_fd
= open(d
, O_TMPFILE
|O_CLOEXEC
|O_NOCTTY
|O_RDWR
, 0600);
270 _cleanup_free_
char *t
= NULL
;
272 r
= tempfn_random(path
, NULL
, &t
);
276 new_fd
= open(t
, O_CLOEXEC
|O_CREAT
|O_NOCTTY
|O_RDWR
, 0600);
283 r
= btrfs_reflink(fd
, new_fd
);
292 int raw_export_start(RawExport
*e
, const char *path
, int fd
, ImportCompressType compress
) {
293 _cleanup_close_
int sfd
= -1, tfd
= -1;
299 assert(compress
< _IMPORT_COMPRESS_TYPE_MAX
);
300 assert(compress
!= IMPORT_COMPRESS_UNKNOWN
);
302 if (e
->output_fd
>= 0)
305 r
= fd_nonblock(fd
, true);
309 r
= free_and_strdup(&e
->path
, path
);
313 sfd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
317 if (fstat(sfd
, &e
->st
) < 0)
319 if (!S_ISREG(e
->st
.st_mode
))
322 /* Try to take a reflink snapshot of the file, if we can t make the export atomic */
323 tfd
= reflink_snapshot(sfd
, path
);
332 r
= import_compress_init(&e
->compress
, compress
);
336 r
= sd_event_add_io(e
->event
, &e
->output_event_source
, fd
, EPOLLOUT
, raw_export_on_output
, e
);
338 r
= sd_event_add_defer(e
->event
, &e
->output_event_source
, raw_export_on_defer
, e
);
342 r
= sd_event_source_set_enabled(e
->output_event_source
, SD_EVENT_ON
);