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