]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/export-raw.c
bdad19f1eaf44b5c7662a77e306460d54fef5625
[thirdparty/systemd.git] / src / import / export-raw.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <sys/sendfile.h>
22
23 /* When we include libgen.h because we need dirname() we immediately
24 * undefine basename() since libgen.h defines it as a macro to the POSIX
25 * version which is really broken. We prefer GNU basename(). */
26 #include <libgen.h>
27 #undef basename
28
29 #include "sd-daemon.h"
30
31 #include "alloc-util.h"
32 #include "btrfs-util.h"
33 #include "copy.h"
34 #include "export-raw.h"
35 #include "fd-util.h"
36 #include "fileio.h"
37 #include "import-common.h"
38 #include "missing.h"
39 #include "ratelimit.h"
40 #include "stat-util.h"
41 #include "string-util.h"
42 #include "util.h"
43
44 #define COPY_BUFFER_SIZE (16*1024)
45
46 struct RawExport {
47 sd_event *event;
48
49 RawExportFinished on_finished;
50 void *userdata;
51
52 char *path;
53
54 int input_fd;
55 int output_fd;
56
57 ImportCompress compress;
58
59 sd_event_source *output_event_source;
60
61 void *buffer;
62 size_t buffer_size;
63 size_t buffer_allocated;
64
65 uint64_t written_compressed;
66 uint64_t written_uncompressed;
67
68 unsigned last_percent;
69 RateLimit progress_rate_limit;
70
71 struct stat st;
72
73 bool eof;
74 bool tried_reflink;
75 bool tried_sendfile;
76 };
77
78 RawExport *raw_export_unref(RawExport *e) {
79 if (!e)
80 return NULL;
81
82 sd_event_source_unref(e->output_event_source);
83
84 import_compress_free(&e->compress);
85
86 sd_event_unref(e->event);
87
88 safe_close(e->input_fd);
89
90 free(e->buffer);
91 free(e->path);
92 return mfree(e);
93 }
94
95 int raw_export_new(
96 RawExport **ret,
97 sd_event *event,
98 RawExportFinished on_finished,
99 void *userdata) {
100
101 _cleanup_(raw_export_unrefp) RawExport *e = NULL;
102 int r;
103
104 assert(ret);
105
106 e = new0(RawExport, 1);
107 if (!e)
108 return -ENOMEM;
109
110 e->output_fd = e->input_fd = -1;
111 e->on_finished = on_finished;
112 e->userdata = userdata;
113
114 RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
115 e->last_percent = (unsigned) -1;
116
117 if (event)
118 e->event = sd_event_ref(event);
119 else {
120 r = sd_event_default(&e->event);
121 if (r < 0)
122 return r;
123 }
124
125 *ret = TAKE_PTR(e);
126
127 return 0;
128 }
129
130 static 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
151 static 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
238 finish:
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
252 static 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
258 static int raw_export_on_defer(sd_event_source *s, void *userdata) {
259 RawExport *i = userdata;
260
261 return raw_export_process(i);
262 }
263
264 static 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
275 r = tempfn_random(path, NULL, &t);
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
295 int 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 r = stat_verify_regular(&e->st);
323 if (r < 0)
324 return r;
325
326 /* Try to take a reflink snapshot of the file, if we can t make the export atomic */
327 tfd = reflink_snapshot(sfd, path);
328 if (tfd >= 0)
329 e->input_fd = TAKE_FD(tfd);
330 else
331 e->input_fd = TAKE_FD(sfd);
332
333 r = import_compress_init(&e->compress, compress);
334 if (r < 0)
335 return r;
336
337 r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, raw_export_on_output, e);
338 if (r == -EPERM) {
339 r = sd_event_add_defer(e->event, &e->output_event_source, raw_export_on_defer, e);
340 if (r < 0)
341 return r;
342
343 r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON);
344 }
345 if (r < 0)
346 return r;
347
348 e->output_fd = fd;
349 return r;
350 }