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