]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/export-raw.c
Merge pull request #1880 from fsateler/sysctl-doc
[thirdparty/systemd.git] / src / import / export-raw.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/sendfile.h>
23 #include <libgen.h>
24 #undef basename
25
26 #include "sd-daemon.h"
27
28 #include "alloc-util.h"
29 #include "btrfs-util.h"
30 #include "copy.h"
31 #include "export-raw.h"
32 #include "fd-util.h"
33 #include "fileio.h"
34 #include "import-common.h"
35 #include "ratelimit.h"
36 #include "string-util.h"
37 #include "util.h"
38
39 #define COPY_BUFFER_SIZE (16*1024)
40
41 struct RawExport {
42 sd_event *event;
43
44 RawExportFinished on_finished;
45 void *userdata;
46
47 char *path;
48
49 int input_fd;
50 int output_fd;
51
52 ImportCompress compress;
53
54 sd_event_source *output_event_source;
55
56 void *buffer;
57 size_t buffer_size;
58 size_t buffer_allocated;
59
60 uint64_t written_compressed;
61 uint64_t written_uncompressed;
62
63 unsigned last_percent;
64 RateLimit progress_rate_limit;
65
66 struct stat st;
67
68 bool eof;
69 bool tried_reflink;
70 bool tried_sendfile;
71 };
72
73 RawExport *raw_export_unref(RawExport *e) {
74 if (!e)
75 return NULL;
76
77 sd_event_source_unref(e->output_event_source);
78
79 import_compress_free(&e->compress);
80
81 sd_event_unref(e->event);
82
83 safe_close(e->input_fd);
84
85 free(e->buffer);
86 free(e->path);
87 free(e);
88
89 return NULL;
90 }
91
92 int raw_export_new(
93 RawExport **ret,
94 sd_event *event,
95 RawExportFinished on_finished,
96 void *userdata) {
97
98 _cleanup_(raw_export_unrefp) RawExport *e = NULL;
99 int r;
100
101 assert(ret);
102
103 e = new0(RawExport, 1);
104 if (!e)
105 return -ENOMEM;
106
107 e->output_fd = e->input_fd = -1;
108 e->on_finished = on_finished;
109 e->userdata = userdata;
110
111 RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
112 e->last_percent = (unsigned) -1;
113
114 if (event)
115 e->event = sd_event_ref(event);
116 else {
117 r = sd_event_default(&e->event);
118 if (r < 0)
119 return r;
120 }
121
122 *ret = e;
123 e = NULL;
124
125 return 0;
126 }
127
128 static void raw_export_report_progress(RawExport *e) {
129 unsigned percent;
130 assert(e);
131
132 if (e->written_uncompressed >= (uint64_t) e->st.st_size)
133 percent = 100;
134 else
135 percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / (uint64_t) e->st.st_size);
136
137 if (percent == e->last_percent)
138 return;
139
140 if (!ratelimit_test(&e->progress_rate_limit))
141 return;
142
143 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
144 log_info("Exported %u%%.", percent);
145
146 e->last_percent = percent;
147 }
148
149 static int raw_export_process(RawExport *e) {
150 ssize_t l;
151 int r;
152
153 assert(e);
154
155 if (!e->tried_reflink && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
156
157 /* If we shall take an uncompressed snapshot we can
158 * reflink source to destination directly. Let's see
159 * if this works. */
160
161 r = btrfs_reflink(e->input_fd, e->output_fd);
162 if (r >= 0) {
163 r = 0;
164 goto finish;
165 }
166
167 e->tried_reflink = true;
168 }
169
170 if (!e->tried_sendfile && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
171
172 l = sendfile(e->output_fd, e->input_fd, NULL, COPY_BUFFER_SIZE);
173 if (l < 0) {
174 if (errno == EAGAIN)
175 return 0;
176
177 e->tried_sendfile = true;
178 } else if (l == 0) {
179 r = 0;
180 goto finish;
181 } else {
182 e->written_uncompressed += l;
183 e->written_compressed += l;
184
185 raw_export_report_progress(e);
186
187 return 0;
188 }
189 }
190
191 while (e->buffer_size <= 0) {
192 uint8_t input[COPY_BUFFER_SIZE];
193
194 if (e->eof) {
195 r = 0;
196 goto finish;
197 }
198
199 l = read(e->input_fd, input, sizeof(input));
200 if (l < 0) {
201 r = log_error_errno(errno, "Failed to read raw file: %m");
202 goto finish;
203 }
204
205 if (l == 0) {
206 e->eof = true;
207 r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated);
208 } else {
209 e->written_uncompressed += l;
210 r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated);
211 }
212 if (r < 0) {
213 r = log_error_errno(r, "Failed to encode: %m");
214 goto finish;
215 }
216 }
217
218 l = write(e->output_fd, e->buffer, e->buffer_size);
219 if (l < 0) {
220 if (errno == EAGAIN)
221 return 0;
222
223 r = log_error_errno(errno, "Failed to write output file: %m");
224 goto finish;
225 }
226
227 assert((size_t) l <= e->buffer_size);
228 memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l);
229 e->buffer_size -= l;
230 e->written_compressed += l;
231
232 raw_export_report_progress(e);
233
234 return 0;
235
236 finish:
237 if (r >= 0) {
238 (void) copy_times(e->input_fd, e->output_fd);
239 (void) copy_xattr(e->input_fd, e->output_fd);
240 }
241
242 if (e->on_finished)
243 e->on_finished(e, r, e->userdata);
244 else
245 sd_event_exit(e->event, r);
246
247 return 0;
248 }
249
250 static int raw_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
251 RawExport *i = userdata;
252
253 return raw_export_process(i);
254 }
255
256 static int raw_export_on_defer(sd_event_source *s, void *userdata) {
257 RawExport *i = userdata;
258
259 return raw_export_process(i);
260 }
261
262 static int reflink_snapshot(int fd, const char *path) {
263 char *p, *d;
264 int new_fd, r;
265
266 p = strdupa(path);
267 d = dirname(p);
268
269 new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600);
270 if (new_fd < 0) {
271 _cleanup_free_ char *t = NULL;
272
273 r = tempfn_random(path, NULL, &t);
274 if (r < 0)
275 return r;
276
277 new_fd = open(t, O_CLOEXEC|O_CREAT|O_NOCTTY|O_RDWR, 0600);
278 if (new_fd < 0)
279 return -errno;
280
281 (void) unlink(t);
282 }
283
284 r = btrfs_reflink(fd, new_fd);
285 if (r < 0) {
286 safe_close(new_fd);
287 return r;
288 }
289
290 return new_fd;
291 }
292
293 int raw_export_start(RawExport *e, const char *path, int fd, ImportCompressType compress) {
294 _cleanup_close_ int sfd = -1, tfd = -1;
295 int r;
296
297 assert(e);
298 assert(path);
299 assert(fd >= 0);
300 assert(compress < _IMPORT_COMPRESS_TYPE_MAX);
301 assert(compress != IMPORT_COMPRESS_UNKNOWN);
302
303 if (e->output_fd >= 0)
304 return -EBUSY;
305
306 r = fd_nonblock(fd, true);
307 if (r < 0)
308 return r;
309
310 r = free_and_strdup(&e->path, path);
311 if (r < 0)
312 return r;
313
314 sfd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
315 if (sfd < 0)
316 return -errno;
317
318 if (fstat(sfd, &e->st) < 0)
319 return -errno;
320 if (!S_ISREG(e->st.st_mode))
321 return -ENOTTY;
322
323 /* Try to take a reflink snapshot of the file, if we can t make the export atomic */
324 tfd = reflink_snapshot(sfd, path);
325 if (tfd >= 0) {
326 e->input_fd = tfd;
327 tfd = -1;
328 } else {
329 e->input_fd = sfd;
330 sfd = -1;
331 }
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 }