]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/export-raw.c
Add SPDX license identifiers to source files under the LGPL
[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 "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 return mfree(e);
92 }
93
94 int raw_export_new(
95 RawExport **ret,
96 sd_event *event,
97 RawExportFinished on_finished,
98 void *userdata) {
99
100 _cleanup_(raw_export_unrefp) RawExport *e = NULL;
101 int r;
102
103 assert(ret);
104
105 e = new0(RawExport, 1);
106 if (!e)
107 return -ENOMEM;
108
109 e->output_fd = e->input_fd = -1;
110 e->on_finished = on_finished;
111 e->userdata = userdata;
112
113 RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
114 e->last_percent = (unsigned) -1;
115
116 if (event)
117 e->event = sd_event_ref(event);
118 else {
119 r = sd_event_default(&e->event);
120 if (r < 0)
121 return r;
122 }
123
124 *ret = e;
125 e = NULL;
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 if (!S_ISREG(e->st.st_mode))
323 return -ENOTTY;
324
325 /* Try to take a reflink snapshot of the file, if we can t make the export atomic */
326 tfd = reflink_snapshot(sfd, path);
327 if (tfd >= 0) {
328 e->input_fd = tfd;
329 tfd = -1;
330 } else {
331 e->input_fd = sfd;
332 sfd = -1;
333 }
334
335 r = import_compress_init(&e->compress, compress);
336 if (r < 0)
337 return r;
338
339 r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, raw_export_on_output, e);
340 if (r == -EPERM) {
341 r = sd_event_add_defer(e->event, &e->output_event_source, raw_export_on_defer, e);
342 if (r < 0)
343 return r;
344
345 r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON);
346 }
347 if (r < 0)
348 return r;
349
350 e->output_fd = fd;
351 return r;
352 }