chiark / gitweb /
timedatectl: remove unused variables
[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_source_unref(i->input_event_source);
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_unref(i->event);
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, 100 * 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         r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
205         if (r < 0)
206                 return log_error_errno(r, "Failed to move image into place: %m");
207
208         free(i->temp_path);
209         i->temp_path = NULL;
210
211         return 0;
212 }
213
214 static int tar_import_fork_tar(TarImport *i) {
215         int r;
216
217         assert(i);
218
219         assert(!i->final_path);
220         assert(!i->temp_path);
221         assert(i->tar_fd < 0);
222
223         i->final_path = strjoin(i->image_root, "/", i->local, NULL);
224         if (!i->final_path)
225                 return log_oom();
226
227         r = tempfn_random(i->final_path, &i->temp_path);
228         if (r < 0)
229                 return log_oom();
230
231         (void) mkdir_parents_label(i->temp_path, 0700);
232
233         r = btrfs_subvol_make(i->temp_path);
234         if (r == -ENOTTY) {
235                 if (mkdir(i->temp_path, 0755) < 0)
236                         return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
237         } else if (r < 0)
238                 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
239
240         i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
241         if (i->tar_fd < 0)
242                 return i->tar_fd;
243
244         return 0;
245 }
246
247 static int tar_import_write(const void *p, size_t sz, void *userdata) {
248         TarImport *i = userdata;
249         int r;
250
251         if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) {
252                 i->written_since_last_grow = 0;
253                 grow_machine_directory();
254         }
255
256         r = loop_write(i->tar_fd, p, sz, false);
257         if (r < 0)
258                 return r;
259
260         i->written_uncompressed += sz;
261         i->written_since_last_grow += sz;
262
263         return 0;
264 }
265
266 static int tar_import_process(TarImport *i) {
267         ssize_t l;
268         int r;
269
270         assert(i);
271         assert(i->buffer_size < sizeof(i->buffer));
272
273         l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size);
274         if (l < 0) {
275                 if (errno == EAGAIN)
276                         return 0;
277
278                 r = log_error_errno(errno, "Failed to read input file: %m");
279                 goto finish;
280         }
281         if (l == 0) {
282                 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
283                         log_error("Premature end of file: %m");
284                         r = -EIO;
285                         goto finish;
286                 }
287
288                 r = tar_import_finish(i);
289                 goto finish;
290         }
291
292         i->buffer_size += l;
293
294         if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
295                 r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size);
296                 if (r < 0) {
297                         log_error("Failed to detect file compression: %m");
298                         goto finish;
299                 }
300                 if (r == 0) /* Need more data */
301                         return 0;
302
303                 r = tar_import_fork_tar(i);
304                 if (r < 0)
305                         goto finish;
306         }
307
308         r = import_uncompress(&i->compress, i->buffer, i->buffer_size, tar_import_write, i);
309         if (r < 0) {
310                 log_error_errno(r, "Failed to decode and write: %m");
311                 goto finish;
312         }
313
314         i->written_compressed += i->buffer_size;
315         i->buffer_size = 0;
316
317         tar_import_report_progress(i);
318
319         return 0;
320
321 finish:
322         if (i->on_finished)
323                 i->on_finished(i, r, i->userdata);
324         else
325                 sd_event_exit(i->event, r);
326
327         return 0;
328 }
329
330 static int tar_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
331         TarImport *i = userdata;
332
333         return tar_import_process(i);
334 }
335
336 static int tar_import_on_defer(sd_event_source *s, void *userdata) {
337         TarImport *i = userdata;
338
339         return tar_import_process(i);
340 }
341
342 int tar_import_start(TarImport *i, int fd, const char *local, bool force_local, bool read_only) {
343         int r;
344
345         assert(i);
346         assert(fd >= 0);
347         assert(local);
348
349         if (!machine_name_is_valid(local))
350                 return -EINVAL;
351
352         if (i->input_fd >= 0)
353                 return -EBUSY;
354
355         r = fd_nonblock(fd, true);
356         if (r < 0)
357                 return r;
358
359         r = free_and_strdup(&i->local, local);
360         if (r < 0)
361                 return r;
362         i->force_local = force_local;
363         i->read_only = read_only;
364
365         if (fstat(fd, &i->st) < 0)
366                 return -errno;
367
368         r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i);
369         if (r == -EPERM) {
370                 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
371                 r = sd_event_add_defer(i->event, &i->input_event_source, tar_import_on_defer, i);
372                 if (r < 0)
373                         return r;
374
375                 r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON);
376         }
377         if (r < 0)
378                 return r;
379
380         i->input_fd = fd;
381         return r;
382 }