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