]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/export-raw.c
Merge pull request #8222 from poettering/journal-by-inode
[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 = e;
126 e = NULL;
127
128 return 0;
129 }
130
131 static void raw_export_report_progress(RawExport *e) {
132 unsigned percent;
133 assert(e);
134
135 if (e->written_uncompressed >= (uint64_t) e->st.st_size)
136 percent = 100;
137 else
138 percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / (uint64_t) e->st.st_size);
139
140 if (percent == e->last_percent)
141 return;
142
143 if (!ratelimit_test(&e->progress_rate_limit))
144 return;
145
146 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
147 log_info("Exported %u%%.", percent);
148
149 e->last_percent = percent;
150 }
151
152 static int raw_export_process(RawExport *e) {
153 ssize_t l;
154 int r;
155
156 assert(e);
157
158 if (!e->tried_reflink && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
159
160 /* If we shall take an uncompressed snapshot we can
161 * reflink source to destination directly. Let's see
162 * if this works. */
163
164 r = btrfs_reflink(e->input_fd, e->output_fd);
165 if (r >= 0) {
166 r = 0;
167 goto finish;
168 }
169
170 e->tried_reflink = true;
171 }
172
173 if (!e->tried_sendfile && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
174
175 l = sendfile(e->output_fd, e->input_fd, NULL, COPY_BUFFER_SIZE);
176 if (l < 0) {
177 if (errno == EAGAIN)
178 return 0;
179
180 e->tried_sendfile = true;
181 } else if (l == 0) {
182 r = 0;
183 goto finish;
184 } else {
185 e->written_uncompressed += l;
186 e->written_compressed += l;
187
188 raw_export_report_progress(e);
189
190 return 0;
191 }
192 }
193
194 while (e->buffer_size <= 0) {
195 uint8_t input[COPY_BUFFER_SIZE];
196
197 if (e->eof) {
198 r = 0;
199 goto finish;
200 }
201
202 l = read(e->input_fd, input, sizeof(input));
203 if (l < 0) {
204 r = log_error_errno(errno, "Failed to read raw file: %m");
205 goto finish;
206 }
207
208 if (l == 0) {
209 e->eof = true;
210 r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated);
211 } else {
212 e->written_uncompressed += l;
213 r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated);
214 }
215 if (r < 0) {
216 r = log_error_errno(r, "Failed to encode: %m");
217 goto finish;
218 }
219 }
220
221 l = write(e->output_fd, e->buffer, e->buffer_size);
222 if (l < 0) {
223 if (errno == EAGAIN)
224 return 0;
225
226 r = log_error_errno(errno, "Failed to write output file: %m");
227 goto finish;
228 }
229
230 assert((size_t) l <= e->buffer_size);
231 memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l);
232 e->buffer_size -= l;
233 e->written_compressed += l;
234
235 raw_export_report_progress(e);
236
237 return 0;
238
239 finish:
240 if (r >= 0) {
241 (void) copy_times(e->input_fd, e->output_fd);
242 (void) copy_xattr(e->input_fd, e->output_fd);
243 }
244
245 if (e->on_finished)
246 e->on_finished(e, r, e->userdata);
247 else
248 sd_event_exit(e->event, r);
249
250 return 0;
251 }
252
253 static int raw_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
254 RawExport *i = userdata;
255
256 return raw_export_process(i);
257 }
258
259 static int raw_export_on_defer(sd_event_source *s, void *userdata) {
260 RawExport *i = userdata;
261
262 return raw_export_process(i);
263 }
264
265 static int reflink_snapshot(int fd, const char *path) {
266 char *p, *d;
267 int new_fd, r;
268
269 p = strdupa(path);
270 d = dirname(p);
271
272 new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600);
273 if (new_fd < 0) {
274 _cleanup_free_ char *t = NULL;
275
276 r = tempfn_random(path, NULL, &t);
277 if (r < 0)
278 return r;
279
280 new_fd = open(t, O_CLOEXEC|O_CREAT|O_NOCTTY|O_RDWR, 0600);
281 if (new_fd < 0)
282 return -errno;
283
284 (void) unlink(t);
285 }
286
287 r = btrfs_reflink(fd, new_fd);
288 if (r < 0) {
289 safe_close(new_fd);
290 return r;
291 }
292
293 return new_fd;
294 }
295
296 int raw_export_start(RawExport *e, const char *path, int fd, ImportCompressType compress) {
297 _cleanup_close_ int sfd = -1, tfd = -1;
298 int r;
299
300 assert(e);
301 assert(path);
302 assert(fd >= 0);
303 assert(compress < _IMPORT_COMPRESS_TYPE_MAX);
304 assert(compress != IMPORT_COMPRESS_UNKNOWN);
305
306 if (e->output_fd >= 0)
307 return -EBUSY;
308
309 r = fd_nonblock(fd, true);
310 if (r < 0)
311 return r;
312
313 r = free_and_strdup(&e->path, path);
314 if (r < 0)
315 return r;
316
317 sfd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
318 if (sfd < 0)
319 return -errno;
320
321 if (fstat(sfd, &e->st) < 0)
322 return -errno;
323 r = stat_verify_regular(&e->st);
324 if (r < 0)
325 return r;
326
327 /* Try to take a reflink snapshot of the file, if we can t make the export atomic */
328 tfd = reflink_snapshot(sfd, path);
329 if (tfd >= 0) {
330 e->input_fd = tfd;
331 tfd = -1;
332 } else {
333 e->input_fd = sfd;
334 sfd = -1;
335 }
336
337 r = import_compress_init(&e->compress, compress);
338 if (r < 0)
339 return r;
340
341 r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, raw_export_on_output, e);
342 if (r == -EPERM) {
343 r = sd_event_add_defer(e->event, &e->output_event_source, raw_export_on_defer, e);
344 if (r < 0)
345 return r;
346
347 r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON);
348 }
349 if (r < 0)
350 return r;
351
352 e->output_fd = fd;
353 return r;
354 }