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