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