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