chiark / gitweb /
fsck: use _cleanup_close_pair_ where appropriate
[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         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_x(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                 if (errno == EAGAIN)
275                         return 0;
276
277                 r = log_error_errno(errno, "Failed to read input file: %m");
278                 goto finish;
279         }
280         if (l == 0) {
281                 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
282                         log_error("Premature end of file: %m");
283                         r = -EIO;
284                         goto finish;
285                 }
286
287                 r = tar_import_finish(i);
288                 goto finish;
289         }
290
291         i->buffer_size += l;
292
293         if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
294                 r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size);
295                 if (r < 0) {
296                         log_error("Failed to detect file compression: %m");
297                         goto finish;
298                 }
299                 if (r == 0) /* Need more data */
300                         return 0;
301
302                 r = tar_import_fork_tar(i);
303                 if (r < 0)
304                         goto finish;
305         }
306
307         r = import_uncompress(&i->compress, i->buffer, i->buffer_size, tar_import_write, i);
308         if (r < 0) {
309                 log_error_errno(r, "Failed to decode and write: %m");
310                 goto finish;
311         }
312
313         i->written_compressed += i->buffer_size;
314         i->buffer_size = 0;
315
316         tar_import_report_progress(i);
317
318         return 0;
319
320 finish:
321         if (i->on_finished)
322                 i->on_finished(i, r, i->userdata);
323         else
324                 sd_event_exit(i->event, r);
325
326         return 0;
327 }
328
329 static int tar_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
330         TarImport *i = userdata;
331
332         return tar_import_process(i);
333 }
334
335 static int tar_import_on_defer(sd_event_source *s, void *userdata) {
336         TarImport *i = userdata;
337
338         return tar_import_process(i);
339 }
340
341 int tar_import_start(TarImport *i, int fd, const char *local, bool force_local, bool read_only) {
342         int r;
343
344         assert(i);
345         assert(fd >= 0);
346         assert(local);
347
348         if (!machine_name_is_valid(local))
349                 return -EINVAL;
350
351         if (i->input_fd >= 0)
352                 return -EBUSY;
353
354         r = fd_nonblock(fd, true);
355         if (r < 0)
356                 return r;
357
358         r = free_and_strdup(&i->local, local);
359         if (r < 0)
360                 return r;
361         i->force_local = force_local;
362         i->read_only = read_only;
363
364         if (fstat(fd, &i->st) < 0)
365                 return -errno;
366
367         r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i);
368         if (r == -EPERM) {
369                 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
370                 r = sd_event_add_defer(i->event, &i->input_event_source, tar_import_on_defer, i);
371                 if (r < 0)
372                         return r;
373
374                 r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON);
375         }
376         if (r < 0)
377                 return r;
378
379         i->input_fd = fd;
380         return r;
381 }