2 This file is part of systemd.
4 Copyright 2015 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include <sys/sendfile.h>
22 /* When we include libgen.h because we need dirname() we immediately
23 * undefine basename() since libgen.h defines it as a macro to the POSIX
24 * version which is really broken. We prefer GNU basename(). */
28 #include "sd-daemon.h"
30 #include "alloc-util.h"
31 #include "btrfs-util.h"
33 #include "export-raw.h"
36 #include "import-common.h"
37 #include "ratelimit.h"
38 #include "string-util.h"
41 #define COPY_BUFFER_SIZE (16*1024)
46 RawExportFinished on_finished
;
54 ImportCompress compress
;
56 sd_event_source
*output_event_source
;
60 size_t buffer_allocated
;
62 uint64_t written_compressed
;
63 uint64_t written_uncompressed
;
65 unsigned last_percent
;
66 RateLimit progress_rate_limit
;
75 RawExport
*raw_export_unref(RawExport
*e
) {
79 sd_event_source_unref(e
->output_event_source
);
81 import_compress_free(&e
->compress
);
83 sd_event_unref(e
->event
);
85 safe_close(e
->input_fd
);
97 RawExportFinished on_finished
,
100 _cleanup_(raw_export_unrefp
) RawExport
*e
= NULL
;
105 e
= new0(RawExport
, 1);
109 e
->output_fd
= e
->input_fd
= -1;
110 e
->on_finished
= on_finished
;
111 e
->userdata
= userdata
;
113 RATELIMIT_INIT(e
->progress_rate_limit
, 100 * USEC_PER_MSEC
, 1);
114 e
->last_percent
= (unsigned) -1;
117 e
->event
= sd_event_ref(event
);
119 r
= sd_event_default(&e
->event
);
130 static void raw_export_report_progress(RawExport
*e
) {
134 if (e
->written_uncompressed
>= (uint64_t) e
->st
.st_size
)
137 percent
= (unsigned) ((e
->written_uncompressed
* UINT64_C(100)) / (uint64_t) e
->st
.st_size
);
139 if (percent
== e
->last_percent
)
142 if (!ratelimit_test(&e
->progress_rate_limit
))
145 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
146 log_info("Exported %u%%.", percent
);
148 e
->last_percent
= percent
;
151 static int raw_export_process(RawExport
*e
) {
157 if (!e
->tried_reflink
&& e
->compress
.type
== IMPORT_COMPRESS_UNCOMPRESSED
) {
159 /* If we shall take an uncompressed snapshot we can
160 * reflink source to destination directly. Let's see
163 r
= btrfs_reflink(e
->input_fd
, e
->output_fd
);
169 e
->tried_reflink
= true;
172 if (!e
->tried_sendfile
&& e
->compress
.type
== IMPORT_COMPRESS_UNCOMPRESSED
) {
174 l
= sendfile(e
->output_fd
, e
->input_fd
, NULL
, COPY_BUFFER_SIZE
);
179 e
->tried_sendfile
= true;
184 e
->written_uncompressed
+= l
;
185 e
->written_compressed
+= l
;
187 raw_export_report_progress(e
);
193 while (e
->buffer_size
<= 0) {
194 uint8_t input
[COPY_BUFFER_SIZE
];
201 l
= read(e
->input_fd
, input
, sizeof(input
));
203 r
= log_error_errno(errno
, "Failed to read raw file: %m");
209 r
= import_compress_finish(&e
->compress
, &e
->buffer
, &e
->buffer_size
, &e
->buffer_allocated
);
211 e
->written_uncompressed
+= l
;
212 r
= import_compress(&e
->compress
, input
, l
, &e
->buffer
, &e
->buffer_size
, &e
->buffer_allocated
);
215 r
= log_error_errno(r
, "Failed to encode: %m");
220 l
= write(e
->output_fd
, e
->buffer
, e
->buffer_size
);
225 r
= log_error_errno(errno
, "Failed to write output file: %m");
229 assert((size_t) l
<= e
->buffer_size
);
230 memmove(e
->buffer
, (uint8_t*) e
->buffer
+ l
, e
->buffer_size
- l
);
232 e
->written_compressed
+= l
;
234 raw_export_report_progress(e
);
240 (void) copy_times(e
->input_fd
, e
->output_fd
);
241 (void) copy_xattr(e
->input_fd
, e
->output_fd
);
245 e
->on_finished(e
, r
, e
->userdata
);
247 sd_event_exit(e
->event
, r
);
252 static int raw_export_on_output(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
253 RawExport
*i
= userdata
;
255 return raw_export_process(i
);
258 static int raw_export_on_defer(sd_event_source
*s
, void *userdata
) {
259 RawExport
*i
= userdata
;
261 return raw_export_process(i
);
264 static int reflink_snapshot(int fd
, const char *path
) {
271 new_fd
= open(d
, O_TMPFILE
|O_CLOEXEC
|O_NOCTTY
|O_RDWR
, 0600);
273 _cleanup_free_
char *t
= NULL
;
275 r
= tempfn_random(path
, NULL
, &t
);
279 new_fd
= open(t
, O_CLOEXEC
|O_CREAT
|O_NOCTTY
|O_RDWR
, 0600);
286 r
= btrfs_reflink(fd
, new_fd
);
295 int raw_export_start(RawExport
*e
, const char *path
, int fd
, ImportCompressType compress
) {
296 _cleanup_close_
int sfd
= -1, tfd
= -1;
302 assert(compress
< _IMPORT_COMPRESS_TYPE_MAX
);
303 assert(compress
!= IMPORT_COMPRESS_UNKNOWN
);
305 if (e
->output_fd
>= 0)
308 r
= fd_nonblock(fd
, true);
312 r
= free_and_strdup(&e
->path
, path
);
316 sfd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
320 if (fstat(sfd
, &e
->st
) < 0)
322 if (!S_ISREG(e
->st
.st_mode
))
325 /* Try to take a reflink snapshot of the file, if we can t make the export atomic */
326 tfd
= reflink_snapshot(sfd
, path
);
335 r
= import_compress_init(&e
->compress
, compress
);
339 r
= sd_event_add_io(e
->event
, &e
->output_event_source
, fd
, EPOLLOUT
, raw_export_on_output
, e
);
341 r
= sd_event_add_defer(e
->event
, &e
->output_event_source
, raw_export_on_defer
, e
);
345 r
= sd_event_source_set_enabled(e
->output_event_source
, SD_EVENT_ON
);