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