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