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 "sd-daemon.h"
22 #include "alloc-util.h"
23 #include "btrfs-util.h"
24 #include "export-tar.h"
27 #include "import-common.h"
28 #include "process-util.h"
29 #include "ratelimit.h"
30 #include "string-util.h"
33 #define COPY_BUFFER_SIZE (16*1024)
38 TarExportFinished on_finished
;
47 ImportCompress compress
;
49 sd_event_source
*output_event_source
;
53 size_t buffer_allocated
;
55 uint64_t written_compressed
;
56 uint64_t written_uncompressed
;
61 uint64_t quota_referenced
;
63 unsigned last_percent
;
64 RateLimit progress_rate_limit
;
70 TarExport
*tar_export_unref(TarExport
*e
) {
74 sd_event_source_unref(e
->output_event_source
);
77 (void) kill_and_sigcont(e
->tar_pid
, SIGKILL
);
78 (void) wait_for_terminate(e
->tar_pid
, NULL
);
82 (void) btrfs_subvol_remove(e
->temp_path
, BTRFS_REMOVE_QUOTA
);
86 import_compress_free(&e
->compress
);
88 sd_event_unref(e
->event
);
90 safe_close(e
->tar_fd
);
102 TarExportFinished on_finished
,
105 _cleanup_(tar_export_unrefp
) TarExport
*e
= NULL
;
110 e
= new0(TarExport
, 1);
114 e
->output_fd
= e
->tar_fd
= -1;
115 e
->on_finished
= on_finished
;
116 e
->userdata
= userdata
;
117 e
->quota_referenced
= (uint64_t) -1;
119 RATELIMIT_INIT(e
->progress_rate_limit
, 100 * USEC_PER_MSEC
, 1);
120 e
->last_percent
= (unsigned) -1;
123 e
->event
= sd_event_ref(event
);
125 r
= sd_event_default(&e
->event
);
136 static void tar_export_report_progress(TarExport
*e
) {
140 /* Do we have any quota info? If not, we don't know anything about the progress */
141 if (e
->quota_referenced
== (uint64_t) -1)
144 if (e
->written_uncompressed
>= e
->quota_referenced
)
147 percent
= (unsigned) ((e
->written_uncompressed
* UINT64_C(100)) / e
->quota_referenced
);
149 if (percent
== e
->last_percent
)
152 if (!ratelimit_test(&e
->progress_rate_limit
))
155 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
156 log_info("Exported %u%%.", percent
);
158 e
->last_percent
= percent
;
161 static int tar_export_process(TarExport
*e
) {
167 if (!e
->tried_splice
&& e
->compress
.type
== IMPORT_COMPRESS_UNCOMPRESSED
) {
169 l
= splice(e
->tar_fd
, NULL
, e
->output_fd
, NULL
, COPY_BUFFER_SIZE
, 0);
174 e
->tried_splice
= true;
179 e
->written_uncompressed
+= l
;
180 e
->written_compressed
+= l
;
182 tar_export_report_progress(e
);
188 while (e
->buffer_size
<= 0) {
189 uint8_t input
[COPY_BUFFER_SIZE
];
196 l
= read(e
->tar_fd
, input
, sizeof(input
));
198 r
= log_error_errno(errno
, "Failed to read tar file: %m");
204 r
= import_compress_finish(&e
->compress
, &e
->buffer
, &e
->buffer_size
, &e
->buffer_allocated
);
206 e
->written_uncompressed
+= l
;
207 r
= import_compress(&e
->compress
, input
, l
, &e
->buffer
, &e
->buffer_size
, &e
->buffer_allocated
);
210 r
= log_error_errno(r
, "Failed to encode: %m");
215 l
= write(e
->output_fd
, e
->buffer
, e
->buffer_size
);
220 r
= log_error_errno(errno
, "Failed to write output file: %m");
224 assert((size_t) l
<= e
->buffer_size
);
225 memmove(e
->buffer
, (uint8_t*) e
->buffer
+ l
, e
->buffer_size
- l
);
227 e
->written_compressed
+= l
;
229 tar_export_report_progress(e
);
235 e
->on_finished(e
, r
, e
->userdata
);
237 sd_event_exit(e
->event
, r
);
242 static int tar_export_on_output(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
243 TarExport
*i
= userdata
;
245 return tar_export_process(i
);
248 static int tar_export_on_defer(sd_event_source
*s
, void *userdata
) {
249 TarExport
*i
= userdata
;
251 return tar_export_process(i
);
254 int tar_export_start(TarExport
*e
, const char *path
, int fd
, ImportCompressType compress
) {
255 _cleanup_close_
int sfd
= -1;
261 assert(compress
< _IMPORT_COMPRESS_TYPE_MAX
);
262 assert(compress
!= IMPORT_COMPRESS_UNKNOWN
);
264 if (e
->output_fd
>= 0)
267 sfd
= open(path
, O_DIRECTORY
|O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
271 if (fstat(sfd
, &e
->st
) < 0)
274 r
= fd_nonblock(fd
, true);
278 r
= free_and_strdup(&e
->path
, path
);
282 e
->quota_referenced
= (uint64_t) -1;
284 if (e
->st
.st_ino
== 256) { /* might be a btrfs subvolume? */
287 r
= btrfs_subvol_get_subtree_quota_fd(sfd
, 0, &q
);
289 e
->quota_referenced
= q
.referenced
;
291 e
->temp_path
= mfree(e
->temp_path
);
293 r
= tempfn_random(path
, NULL
, &e
->temp_path
);
297 /* Let's try to make a snapshot, if we can, so that the export is atomic */
298 r
= btrfs_subvol_snapshot_fd(sfd
, e
->temp_path
, BTRFS_SNAPSHOT_READ_ONLY
|BTRFS_SNAPSHOT_RECURSIVE
);
300 log_debug_errno(r
, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e
->temp_path
, path
);
301 e
->temp_path
= mfree(e
->temp_path
);
305 r
= import_compress_init(&e
->compress
, compress
);
309 r
= sd_event_add_io(e
->event
, &e
->output_event_source
, fd
, EPOLLOUT
, tar_export_on_output
, e
);
311 r
= sd_event_add_defer(e
->event
, &e
->output_event_source
, tar_export_on_defer
, e
);
315 r
= sd_event_source_set_enabled(e
->output_event_source
, SD_EVENT_ON
);
320 e
->tar_fd
= import_fork_tar_c(e
->temp_path
?: e
->path
, &e
->tar_pid
);
322 e
->output_event_source
= sd_event_source_unref(e
->output_event_source
);