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