]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/export-raw.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / import / export-raw.c
CommitLineData
587fec42
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include <sys/sendfile.h>
23#include <libgen.h>
24#undef basename
25
26#include "sd-daemon.h"
07630cea 27
587fec42
LP
28#include "btrfs-util.h"
29#include "copy.h"
587fec42 30#include "export-raw.h"
07630cea
LP
31#include "import-common.h"
32#include "ratelimit.h"
33#include "string-util.h"
34#include "util.h"
587fec42
LP
35
36#define COPY_BUFFER_SIZE (16*1024)
37
38struct RawExport {
39 sd_event *event;
40
41 RawExportFinished on_finished;
42 void *userdata;
43
44 char *path;
45
46 int input_fd;
47 int output_fd;
48
49 ImportCompress compress;
50
51 sd_event_source *output_event_source;
52
53 void *buffer;
54 size_t buffer_size;
55 size_t buffer_allocated;
56
57 uint64_t written_compressed;
58 uint64_t written_uncompressed;
59
60 unsigned last_percent;
61 RateLimit progress_rate_limit;
62
63 struct stat st;
64
65 bool eof;
66 bool tried_reflink;
67 bool tried_sendfile;
68};
69
70RawExport *raw_export_unref(RawExport *e) {
71 if (!e)
72 return NULL;
73
74 sd_event_source_unref(e->output_event_source);
75
76 import_compress_free(&e->compress);
77
78 sd_event_unref(e->event);
79
80 safe_close(e->input_fd);
81
82 free(e->buffer);
83 free(e->path);
84 free(e);
85
86 return NULL;
87}
88
89int raw_export_new(
90 RawExport **ret,
91 sd_event *event,
92 RawExportFinished on_finished,
93 void *userdata) {
94
95 _cleanup_(raw_export_unrefp) RawExport *e = NULL;
96 int r;
97
98 assert(ret);
99
100 e = new0(RawExport, 1);
101 if (!e)
102 return -ENOMEM;
103
104 e->output_fd = e->input_fd = -1;
105 e->on_finished = on_finished;
106 e->userdata = userdata;
107
108 RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
109 e->last_percent = (unsigned) -1;
110
111 if (event)
112 e->event = sd_event_ref(event);
113 else {
114 r = sd_event_default(&e->event);
115 if (r < 0)
116 return r;
117 }
118
119 *ret = e;
120 e = NULL;
121
122 return 0;
123}
124
125static void raw_export_report_progress(RawExport *e) {
126 unsigned percent;
127 assert(e);
128
129 if (e->written_uncompressed >= (uint64_t) e->st.st_size)
130 percent = 100;
131 else
132 percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / (uint64_t) e->st.st_size);
133
134 if (percent == e->last_percent)
135 return;
136
137 if (!ratelimit_test(&e->progress_rate_limit))
138 return;
139
140 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
141 log_info("Exported %u%%.", percent);
142
143 e->last_percent = percent;
144}
145
146static int raw_export_process(RawExport *e) {
147 ssize_t l;
148 int r;
149
150 assert(e);
151
152 if (!e->tried_reflink && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
153
154 /* If we shall take an uncompressed snapshot we can
155 * reflink source to destination directly. Let's see
156 * if this works. */
157
158 r = btrfs_reflink(e->input_fd, e->output_fd);
159 if (r >= 0) {
160 r = 0;
161 goto finish;
162 }
163
164 e->tried_reflink = true;
165 }
166
167 if (!e->tried_sendfile && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
168
169 l = sendfile(e->output_fd, e->input_fd, NULL, COPY_BUFFER_SIZE);
170 if (l < 0) {
171 if (errno == EAGAIN)
172 return 0;
173
174 e->tried_sendfile = true;
175 } else if (l == 0) {
176 r = 0;
177 goto finish;
178 } else {
179 e->written_uncompressed += l;
180 e->written_compressed += l;
181
182 raw_export_report_progress(e);
183
184 return 0;
185 }
186 }
187
188 while (e->buffer_size <= 0) {
189 uint8_t input[COPY_BUFFER_SIZE];
190
191 if (e->eof) {
192 r = 0;
193 goto finish;
194 }
195
196 l = read(e->input_fd, input, sizeof(input));
197 if (l < 0) {
198 r = log_error_errno(errno, "Failed to read raw file: %m");
199 goto finish;
200 }
201
202 if (l == 0) {
203 e->eof = true;
204 r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated);
205 } else {
206 e->written_uncompressed += l;
207 r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated);
208 }
209 if (r < 0) {
210 r = log_error_errno(r, "Failed to encode: %m");
211 goto finish;
212 }
213 }
214
215 l = write(e->output_fd, e->buffer, e->buffer_size);
216 if (l < 0) {
217 if (errno == EAGAIN)
218 return 0;
219
220 r = log_error_errno(errno, "Failed to write output file: %m");
221 goto finish;
222 }
223
224 assert((size_t) l <= e->buffer_size);
225 memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l);
226 e->buffer_size -= l;
227 e->written_compressed += l;
228
229 raw_export_report_progress(e);
230
231 return 0;
232
233finish:
234 if (r >= 0) {
235 (void) copy_times(e->input_fd, e->output_fd);
236 (void) copy_xattr(e->input_fd, e->output_fd);
237 }
238
239 if (e->on_finished)
240 e->on_finished(e, r, e->userdata);
241 else
242 sd_event_exit(e->event, r);
243
244 return 0;
245}
246
247static int raw_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
248 RawExport *i = userdata;
249
250 return raw_export_process(i);
251}
252
253static int raw_export_on_defer(sd_event_source *s, void *userdata) {
254 RawExport *i = userdata;
255
256 return raw_export_process(i);
257}
258
259static int reflink_snapshot(int fd, const char *path) {
260 char *p, *d;
261 int new_fd, r;
262
263 p = strdupa(path);
264 d = dirname(p);
265
266 new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600);
267 if (new_fd < 0) {
268 _cleanup_free_ char *t = NULL;
269
14bcf25c 270 r = tempfn_random(path, NULL, &t);
587fec42
LP
271 if (r < 0)
272 return r;
273
274 new_fd = open(t, O_CLOEXEC|O_CREAT|O_NOCTTY|O_RDWR, 0600);
275 if (new_fd < 0)
276 return -errno;
277
278 (void) unlink(t);
279 }
280
281 r = btrfs_reflink(fd, new_fd);
282 if (r < 0) {
283 safe_close(new_fd);
284 return r;
285 }
286
287 return new_fd;
288}
289
290int raw_export_start(RawExport *e, const char *path, int fd, ImportCompressType compress) {
291 _cleanup_close_ int sfd = -1, tfd = -1;
292 int r;
293
294 assert(e);
295 assert(path);
296 assert(fd >= 0);
297 assert(compress < _IMPORT_COMPRESS_TYPE_MAX);
298 assert(compress != IMPORT_COMPRESS_UNKNOWN);
299
300 if (e->output_fd >= 0)
301 return -EBUSY;
302
303 r = fd_nonblock(fd, true);
304 if (r < 0)
305 return r;
306
307 r = free_and_strdup(&e->path, path);
308 if (r < 0)
309 return r;
310
311 sfd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
312 if (sfd < 0)
313 return -errno;
314
315 if (fstat(sfd, &e->st) < 0)
316 return -errno;
317 if (!S_ISREG(e->st.st_mode))
318 return -ENOTTY;
319
320 /* Try to take a reflink snapshot of the file, if we can t make the export atomic */
321 tfd = reflink_snapshot(sfd, path);
322 if (tfd >= 0) {
323 e->input_fd = tfd;
324 tfd = -1;
325 } else {
326 e->input_fd = sfd;
327 sfd = -1;
328 }
329
330 r = import_compress_init(&e->compress, compress);
331 if (r < 0)
332 return r;
333
334 r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, raw_export_on_output, e);
335 if (r == -EPERM) {
336 r = sd_event_add_defer(e->event, &e->output_event_source, raw_export_on_defer, e);
337 if (r < 0)
338 return r;
339
340 r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON);
341 }
342 if (r < 0)
343 return r;
344
345 e->output_fd = fd;
346 return r;
347}