]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/export-raw.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[thirdparty/systemd.git] / src / import / export-raw.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
587fec42
LP
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>
cf0fbc49
TA
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(). */
587fec42
LP
26#include <libgen.h>
27#undef basename
28
29#include "sd-daemon.h"
07630cea 30
b5efdb8a 31#include "alloc-util.h"
587fec42
LP
32#include "btrfs-util.h"
33#include "copy.h"
587fec42 34#include "export-raw.h"
3ffd4af2 35#include "fd-util.h"
0d39fa9c 36#include "fileio.h"
07630cea 37#include "import-common.h"
4a6d3523 38#include "missing.h"
07630cea 39#include "ratelimit.h"
3cc44114 40#include "stat-util.h"
07630cea
LP
41#include "string-util.h"
42#include "util.h"
587fec42
LP
43
44#define COPY_BUFFER_SIZE (16*1024)
45
46struct 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
78RawExport *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);
6b430fdb 92 return mfree(e);
587fec42
LP
93}
94
95int 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
1cc6c93a 125 *ret = TAKE_PTR(e);
587fec42
LP
126
127 return 0;
128}
129
130static 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
151static 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
238finish:
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
252static 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
258static int raw_export_on_defer(sd_event_source *s, void *userdata) {
259 RawExport *i = userdata;
260
261 return raw_export_process(i);
262}
263
264static 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
14bcf25c 275 r = tempfn_random(path, NULL, &t);
587fec42
LP
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
295int 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;
3cc44114
LP
322 r = stat_verify_regular(&e->st);
323 if (r < 0)
324 return r;
587fec42
LP
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);
c10d6bdb
LP
328 if (tfd >= 0)
329 e->input_fd = TAKE_FD(tfd);
330 else
331 e->input_fd = TAKE_FD(sfd);
587fec42
LP
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}