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