chiark / gitweb /
importd: add new bus calls for importing local tar and raw images
[elogind.git] / src / import / import-tar.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-tar.h"
37
38 struct TarImport {
39         sd_event *event;
40
41         char *image_root;
42
43         TarImportFinished 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 tar_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         pid_t tar_pid;
72
73         unsigned last_percent;
74         RateLimit progress_rate_limit;
75 };
76
77 TarImport* tar_import_unref(TarImport *i) {
78         if (!i)
79                 return NULL;
80
81         sd_event_unref(i->event);
82
83         if (i->tar_pid > 1) {
84                 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
85                 (void) wait_for_terminate(i->tar_pid, NULL);
86         }
87
88         if (i->temp_path) {
89                 (void) btrfs_subvol_remove(i->temp_path);
90                 (void) rm_rf_dangerous(i->temp_path, false, true, false);
91                 free(i->temp_path);
92         }
93
94         import_compress_free(&i->compress);
95
96         sd_event_source_unref(i->input_event_source);
97
98         safe_close(i->tar_fd);
99
100         free(i->final_path);
101         free(i->image_root);
102         free(i->local);
103         free(i);
104
105         return NULL;
106 }
107
108 int tar_import_new(
109                 TarImport **ret,
110                 sd_event *event,
111                 const char *image_root,
112                 TarImportFinished on_finished,
113                 void *userdata) {
114
115         _cleanup_(tar_import_unrefp) TarImport *i = NULL;
116         int r;
117
118         assert(ret);
119
120         i = new0(TarImport, 1);
121         if (!i)
122                 return -ENOMEM;
123
124         i->input_fd = i->tar_fd = -1;
125         i->on_finished = on_finished;
126         i->userdata = userdata;
127
128         RATELIMIT_INIT(i->progress_rate_limit, 500 * USEC_PER_MSEC, 1);
129         i->last_percent = (unsigned) -1;
130
131         i->image_root = strdup(image_root ?: "/var/lib/machines");
132         if (!i->image_root)
133                 return -ENOMEM;
134
135         i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
136
137         if (event)
138                 i->event = sd_event_ref(event);
139         else {
140                 r = sd_event_default(&i->event);
141                 if (r < 0)
142                         return r;
143         }
144
145         *ret = i;
146         i = NULL;
147
148         return 0;
149 }
150
151 static void tar_import_report_progress(TarImport *i) {
152         unsigned percent;
153         assert(i);
154
155         /* We have no size information, unless the source is a regular file */
156         if (!S_ISREG(i->st.st_mode))
157                 return;
158
159         if (i->written_compressed >= (uint64_t) i->st.st_size)
160                 percent = 100;
161         else
162                 percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
163
164         if (percent == i->last_percent)
165                 return;
166
167         if (!ratelimit_test(&i->progress_rate_limit))
168                 return;
169
170         sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
171         log_info("Imported %u%%.", percent);
172
173         i->last_percent = percent;
174 }
175
176 static int tar_import_finish(TarImport *i) {
177         int r;
178
179         assert(i);
180         assert(i->tar_fd >= 0);
181         assert(i->temp_path);
182         assert(i->final_path);
183
184         i->tar_fd = safe_close(i->tar_fd);
185
186         if (i->tar_pid > 0) {
187                 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
188                 i->tar_pid = 0;
189                 if (r < 0)
190                         return r;
191         }
192
193         if (i->read_only) {
194                 r = import_make_read_only(i->temp_path);
195                 if (r < 0)
196                         return r;
197         }
198
199         if (i->force_local) {
200                 (void) btrfs_subvol_remove(i->final_path);
201                 (void) rm_rf_dangerous(i->final_path, false, true, false);
202         }
203
204         if (renameat2(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path, RENAME_NOREPLACE) < 0)
205                 return log_error_errno(errno, "Failed to move image into place: %m");
206
207         free(i->temp_path);
208         i->temp_path = NULL;
209
210         return 0;
211 }
212
213 static int tar_import_fork_tar(TarImport *i) {
214         int r;
215
216         assert(i);
217
218         assert(!i->final_path);
219         assert(!i->temp_path);
220         assert(i->tar_fd < 0);
221
222         i->final_path = strjoin(i->image_root, "/", i->local, NULL);
223         if (!i->final_path)
224                 return log_oom();
225
226         r = tempfn_random(i->final_path, &i->temp_path);
227         if (r < 0)
228                 return log_oom();
229
230         (void) mkdir_parents_label(i->temp_path, 0700);
231
232         r = btrfs_subvol_make(i->temp_path);
233         if (r == -ENOTTY) {
234                 if (mkdir(i->temp_path, 0755) < 0)
235                         return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
236         } else if (r < 0)
237                 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
238
239         i->tar_fd = import_fork_tar(i->temp_path, &i->tar_pid);
240         if (i->tar_fd < 0)
241                 return i->tar_fd;
242
243         return 0;
244 }
245
246 static int tar_import_write(const void *p, size_t sz, void *userdata) {
247         TarImport *i = userdata;
248         int r;
249
250         if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) {
251                 i->written_since_last_grow = 0;
252                 grow_machine_directory();
253         }
254
255         r = loop_write(i->tar_fd, p, sz, false);
256         if (r < 0)
257                 return r;
258
259         i->written_uncompressed += sz;
260         i->written_since_last_grow += sz;
261
262         return 0;
263 }
264
265 static int tar_import_process(TarImport *i) {
266         ssize_t l;
267         int r;
268
269         assert(i);
270         assert(i->buffer_size < sizeof(i->buffer));
271
272         l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size);
273         if (l < 0) {
274                 r = log_error_errno(errno, "Failed to read input file: %m");
275                 goto finish;
276         }
277         if (l == 0) {
278                 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
279                         log_error("Premature end of file: %m");
280                         r = -EIO;
281                         goto finish;
282                 }
283
284                 r = tar_import_finish(i);
285                 goto finish;
286         }
287
288         i->buffer_size += l;
289
290         if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
291                 r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size);
292                 if (r < 0) {
293                         log_error("Failed to detect file compression: %m");
294                         goto finish;
295                 }
296                 if (r == 0) /* Need more data */
297                         return 0;
298
299                 r = tar_import_fork_tar(i);
300                 if (r < 0)
301                         goto finish;
302         }
303
304         r = import_uncompress(&i->compress, i->buffer, i->buffer_size, tar_import_write, i);
305         if (r < 0) {
306                 log_error_errno(r, "Failed to decode and write: %m");
307                 goto finish;
308         }
309
310         i->written_compressed += i->buffer_size;
311         i->buffer_size = 0;
312
313         tar_import_report_progress(i);
314
315         return 0;
316
317 finish:
318         if (i->on_finished)
319                 i->on_finished(i, r, i->userdata);
320         else
321                 sd_event_exit(i->event, r);
322
323         return 0;
324 }
325
326 static int tar_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
327         TarImport *i = userdata;
328
329         return tar_import_process(i);
330 }
331
332 static int tar_import_on_defer(sd_event_source *s, void *userdata) {
333         TarImport *i = userdata;
334
335         return tar_import_process(i);
336 }
337
338 int tar_import_start(TarImport *i, int fd, const char *local, bool force_local, bool read_only) {
339         int r;
340
341         assert(i);
342         assert(fd >= 0);
343         assert(local);
344
345         if (!machine_name_is_valid(local))
346                 return -EINVAL;
347
348         if (i->input_fd >= 0)
349                 return -EBUSY;
350
351         r = free_and_strdup(&i->local, local);
352         if (r < 0)
353                 return r;
354         i->force_local = force_local;
355         i->read_only = read_only;
356
357         if (fstat(fd, &i->st) < 0)
358                 return -errno;
359
360         r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i);
361         if (r == -EPERM) {
362                 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
363                 r = sd_event_add_defer(i->event, &i->input_event_source, tar_import_on_defer, i);
364                 if (r < 0)
365                         return r;
366
367                 r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON);
368         }
369         if (r < 0)
370                 return r;
371
372         i->input_fd = fd;
373         return r;
374 }