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