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