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