Branch data 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 : : }
|