1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <sys/sendfile.h>
23 /* When we include libgen.h because we need dirname() we immediately
24 * undefine basename() since libgen.h defines it as a macro to the POSIX
25 * version which is really broken. We prefer GNU basename(). */
29 #include "sd-daemon.h"
31 #include "alloc-util.h"
32 #include "btrfs-util.h"
34 #include "export-raw.h"
37 #include "import-common.h"
39 #include "ratelimit.h"
40 #include "stat-util.h"
41 #include "string-util.h"
44 #define COPY_BUFFER_SIZE (16*1024)
49 RawExportFinished on_finished
;
57 ImportCompress compress
;
59 sd_event_source
*output_event_source
;
63 size_t buffer_allocated
;
65 uint64_t written_compressed
;
66 uint64_t written_uncompressed
;
68 unsigned last_percent
;
69 RateLimit progress_rate_limit
;
78 RawExport
*raw_export_unref(RawExport
*e
) {
82 sd_event_source_unref(e
->output_event_source
);
84 import_compress_free(&e
->compress
);
86 sd_event_unref(e
->event
);
88 safe_close(e
->input_fd
);
98 RawExportFinished on_finished
,
101 _cleanup_(raw_export_unrefp
) RawExport
*e
= NULL
;
106 e
= new0(RawExport
, 1);
110 e
->output_fd
= e
->input_fd
= -1;
111 e
->on_finished
= on_finished
;
112 e
->userdata
= userdata
;
114 RATELIMIT_INIT(e
->progress_rate_limit
, 100 * USEC_PER_MSEC
, 1);
115 e
->last_percent
= (unsigned) -1;
118 e
->event
= sd_event_ref(event
);
120 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 r
= stat_verify_regular(&e
->st
);
326 /* Try to take a reflink snapshot of the file, if we can t make the export atomic */
327 tfd
= reflink_snapshot(sfd
, path
);
329 e
->input_fd
= TAKE_FD(tfd
);
331 e
->input_fd
= TAKE_FD(sfd
);
333 r
= import_compress_init(&e
->compress
, compress
);
337 r
= sd_event_add_io(e
->event
, &e
->output_event_source
, fd
, EPOLLOUT
, raw_export_on_output
, e
);
339 r
= sd_event_add_defer(e
->event
, &e
->output_event_source
, raw_export_on_defer
, e
);
343 r
= sd_event_source_set_enabled(e
->output_event_source
, SD_EVENT_ON
);