]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/import-raw.c
util-lib: move a number of fs operations into fs-util.[ch]
[thirdparty/systemd.git] / src / import / import-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 <linux/fs.h>
23
24 #include "sd-daemon.h"
25 #include "sd-event.h"
26
27 #include "btrfs-util.h"
28 #include "chattr-util.h"
29 #include "copy.h"
30 #include "fd-util.h"
31 #include "fileio.h"
32 #include "fs-util.h"
33 #include "hostname-util.h"
34 #include "import-common.h"
35 #include "import-compress.h"
36 #include "import-raw.h"
37 #include "io-util.h"
38 #include "machine-pool.h"
39 #include "mkdir.h"
40 #include "path-util.h"
41 #include "qcow2-util.h"
42 #include "ratelimit.h"
43 #include "rm-rf.h"
44 #include "string-util.h"
45 #include "util.h"
46
47 struct RawImport {
48 sd_event *event;
49
50 char *image_root;
51
52 RawImportFinished on_finished;
53 void *userdata;
54
55 char *local;
56 bool force_local;
57 bool read_only;
58 bool grow_machine_directory;
59
60 char *temp_path;
61 char *final_path;
62
63 int input_fd;
64 int output_fd;
65
66 ImportCompress compress;
67
68 uint64_t written_since_last_grow;
69
70 sd_event_source *input_event_source;
71
72 uint8_t buffer[16*1024];
73 size_t buffer_size;
74
75 uint64_t written_compressed;
76 uint64_t written_uncompressed;
77
78 struct stat st;
79
80 unsigned last_percent;
81 RateLimit progress_rate_limit;
82 };
83
84 RawImport* raw_import_unref(RawImport *i) {
85 if (!i)
86 return NULL;
87
88 sd_event_unref(i->event);
89
90 if (i->temp_path) {
91 (void) unlink(i->temp_path);
92 free(i->temp_path);
93 }
94
95 import_compress_free(&i->compress);
96
97 sd_event_source_unref(i->input_event_source);
98
99 safe_close(i->output_fd);
100
101 free(i->final_path);
102 free(i->image_root);
103 free(i->local);
104 free(i);
105
106 return NULL;
107 }
108
109 int raw_import_new(
110 RawImport **ret,
111 sd_event *event,
112 const char *image_root,
113 RawImportFinished on_finished,
114 void *userdata) {
115
116 _cleanup_(raw_import_unrefp) RawImport *i = NULL;
117 int r;
118
119 assert(ret);
120
121 i = new0(RawImport, 1);
122 if (!i)
123 return -ENOMEM;
124
125 i->input_fd = i->output_fd = -1;
126 i->on_finished = on_finished;
127 i->userdata = userdata;
128
129 RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
130 i->last_percent = (unsigned) -1;
131
132 i->image_root = strdup(image_root ?: "/var/lib/machines");
133 if (!i->image_root)
134 return -ENOMEM;
135
136 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
137
138 if (event)
139 i->event = sd_event_ref(event);
140 else {
141 r = sd_event_default(&i->event);
142 if (r < 0)
143 return r;
144 }
145
146 *ret = i;
147 i = NULL;
148
149 return 0;
150 }
151
152 static void raw_import_report_progress(RawImport *i) {
153 unsigned percent;
154 assert(i);
155
156 /* We have no size information, unless the source is a regular file */
157 if (!S_ISREG(i->st.st_mode))
158 return;
159
160 if (i->written_compressed >= (uint64_t) i->st.st_size)
161 percent = 100;
162 else
163 percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
164
165 if (percent == i->last_percent)
166 return;
167
168 if (!ratelimit_test(&i->progress_rate_limit))
169 return;
170
171 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
172 log_info("Imported %u%%.", percent);
173
174 i->last_percent = percent;
175 }
176
177 static int raw_import_maybe_convert_qcow2(RawImport *i) {
178 _cleanup_close_ int converted_fd = -1;
179 _cleanup_free_ char *t = NULL;
180 int r;
181
182 assert(i);
183
184 r = qcow2_detect(i->output_fd);
185 if (r < 0)
186 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
187 if (r == 0)
188 return 0;
189
190 /* This is a QCOW2 image, let's convert it */
191 r = tempfn_random(i->final_path, NULL, &t);
192 if (r < 0)
193 return log_oom();
194
195 converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
196 if (converted_fd < 0)
197 return log_error_errno(errno, "Failed to create %s: %m", t);
198
199 r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
200 if (r < 0)
201 log_warning_errno(errno, "Failed to set file attributes on %s: %m", t);
202
203 log_info("Unpacking QCOW2 file.");
204
205 r = qcow2_convert(i->output_fd, converted_fd);
206 if (r < 0) {
207 unlink(t);
208 return log_error_errno(r, "Failed to convert qcow2 image: %m");
209 }
210
211 (void) unlink(i->temp_path);
212 free(i->temp_path);
213 i->temp_path = t;
214 t = NULL;
215
216 safe_close(i->output_fd);
217 i->output_fd = converted_fd;
218 converted_fd = -1;
219
220 return 1;
221 }
222
223 static int raw_import_finish(RawImport *i) {
224 int r;
225
226 assert(i);
227 assert(i->output_fd >= 0);
228 assert(i->temp_path);
229 assert(i->final_path);
230
231 /* In case this was a sparse file, make sure the file system is right */
232 if (i->written_uncompressed > 0) {
233 if (ftruncate(i->output_fd, i->written_uncompressed) < 0)
234 return log_error_errno(errno, "Failed to truncate file: %m");
235 }
236
237 r = raw_import_maybe_convert_qcow2(i);
238 if (r < 0)
239 return r;
240
241 if (S_ISREG(i->st.st_mode)) {
242 (void) copy_times(i->input_fd, i->output_fd);
243 (void) copy_xattr(i->input_fd, i->output_fd);
244 }
245
246 if (i->read_only) {
247 r = import_make_read_only_fd(i->output_fd);
248 if (r < 0)
249 return r;
250 }
251
252 if (i->force_local)
253 (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
254
255 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
256 if (r < 0)
257 return log_error_errno(r, "Failed to move image into place: %m");
258
259 i->temp_path = mfree(i->temp_path);
260
261 return 0;
262 }
263
264 static int raw_import_open_disk(RawImport *i) {
265 int r;
266
267 assert(i);
268
269 assert(!i->final_path);
270 assert(!i->temp_path);
271 assert(i->output_fd < 0);
272
273 i->final_path = strjoin(i->image_root, "/", i->local, ".raw", NULL);
274 if (!i->final_path)
275 return log_oom();
276
277 r = tempfn_random(i->final_path, NULL, &i->temp_path);
278 if (r < 0)
279 return log_oom();
280
281 (void) mkdir_parents_label(i->temp_path, 0700);
282
283 i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
284 if (i->output_fd < 0)
285 return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path);
286
287 r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL);
288 if (r < 0)
289 log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
290
291 return 0;
292 }
293
294 static int raw_import_try_reflink(RawImport *i) {
295 off_t p;
296 int r;
297
298 assert(i);
299 assert(i->input_fd >= 0);
300 assert(i->output_fd >= 0);
301
302 if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED)
303 return 0;
304
305 if (!S_ISREG(i->st.st_mode))
306 return 0;
307
308 p = lseek(i->input_fd, 0, SEEK_CUR);
309 if (p == (off_t) -1)
310 return log_error_errno(errno, "Failed to read file offset of input file: %m");
311
312 /* Let's only try a btrfs reflink, if we are reading from the beginning of the file */
313 if ((uint64_t) p != (uint64_t) i->buffer_size)
314 return 0;
315
316 r = btrfs_reflink(i->input_fd, i->output_fd);
317 if (r >= 0)
318 return 1;
319
320 return 0;
321 }
322
323 static int raw_import_write(const void *p, size_t sz, void *userdata) {
324 RawImport *i = userdata;
325 ssize_t n;
326
327 if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) {
328 i->written_since_last_grow = 0;
329 grow_machine_directory();
330 }
331
332 n = sparse_write(i->output_fd, p, sz, 64);
333 if (n < 0)
334 return -errno;
335 if ((size_t) n < sz)
336 return -EIO;
337
338 i->written_uncompressed += sz;
339 i->written_since_last_grow += sz;
340
341 return 0;
342 }
343
344 static int raw_import_process(RawImport *i) {
345 ssize_t l;
346 int r;
347
348 assert(i);
349 assert(i->buffer_size < sizeof(i->buffer));
350
351 l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size);
352 if (l < 0) {
353 if (errno == EAGAIN)
354 return 0;
355
356 r = log_error_errno(errno, "Failed to read input file: %m");
357 goto finish;
358 }
359 if (l == 0) {
360 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
361 log_error("Premature end of file: %m");
362 r = -EIO;
363 goto finish;
364 }
365
366 r = raw_import_finish(i);
367 goto finish;
368 }
369
370 i->buffer_size += l;
371
372 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
373 r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size);
374 if (r < 0) {
375 log_error("Failed to detect file compression: %m");
376 goto finish;
377 }
378 if (r == 0) /* Need more data */
379 return 0;
380
381 r = raw_import_open_disk(i);
382 if (r < 0)
383 goto finish;
384
385 r = raw_import_try_reflink(i);
386 if (r < 0)
387 goto finish;
388 if (r > 0) {
389 r = raw_import_finish(i);
390 goto finish;
391 }
392 }
393
394 r = import_uncompress(&i->compress, i->buffer, i->buffer_size, raw_import_write, i);
395 if (r < 0) {
396 log_error_errno(r, "Failed to decode and write: %m");
397 goto finish;
398 }
399
400 i->written_compressed += i->buffer_size;
401 i->buffer_size = 0;
402
403 raw_import_report_progress(i);
404
405 return 0;
406
407 finish:
408 if (i->on_finished)
409 i->on_finished(i, r, i->userdata);
410 else
411 sd_event_exit(i->event, r);
412
413 return 0;
414 }
415
416 static int raw_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
417 RawImport *i = userdata;
418
419 return raw_import_process(i);
420 }
421
422 static int raw_import_on_defer(sd_event_source *s, void *userdata) {
423 RawImport *i = userdata;
424
425 return raw_import_process(i);
426 }
427
428 int raw_import_start(RawImport *i, int fd, const char *local, bool force_local, bool read_only) {
429 int r;
430
431 assert(i);
432 assert(fd >= 0);
433 assert(local);
434
435 if (!machine_name_is_valid(local))
436 return -EINVAL;
437
438 if (i->input_fd >= 0)
439 return -EBUSY;
440
441 r = fd_nonblock(fd, true);
442 if (r < 0)
443 return r;
444
445 r = free_and_strdup(&i->local, local);
446 if (r < 0)
447 return r;
448 i->force_local = force_local;
449 i->read_only = read_only;
450
451 if (fstat(fd, &i->st) < 0)
452 return -errno;
453
454 r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, raw_import_on_input, i);
455 if (r == -EPERM) {
456 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
457 r = sd_event_add_defer(i->event, &i->input_event_source, raw_import_on_defer, i);
458 if (r < 0)
459 return r;
460
461 r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON);
462 }
463 if (r < 0)
464 return r;
465
466 i->input_fd = fd;
467 return r;
468 }