]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/import-tar.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / import / import-tar.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <linux/fs.h>
22
23 #include "sd-daemon.h"
24 #include "sd-event.h"
25
26 #include "alloc-util.h"
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 return mfree(i);
112 }
113
114 int tar_import_new(
115 TarImport **ret,
116 sd_event *event,
117 const char *image_root,
118 TarImportFinished on_finished,
119 void *userdata) {
120
121 _cleanup_(tar_import_unrefp) TarImport *i = NULL;
122 int r;
123
124 assert(ret);
125
126 i = new0(TarImport, 1);
127 if (!i)
128 return -ENOMEM;
129
130 i->input_fd = i->tar_fd = -1;
131 i->on_finished = on_finished;
132 i->userdata = userdata;
133
134 RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
135 i->last_percent = (unsigned) -1;
136
137 i->image_root = strdup(image_root ?: "/var/lib/machines");
138 if (!i->image_root)
139 return -ENOMEM;
140
141 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
142
143 if (event)
144 i->event = sd_event_ref(event);
145 else {
146 r = sd_event_default(&i->event);
147 if (r < 0)
148 return r;
149 }
150
151 *ret = i;
152 i = NULL;
153
154 return 0;
155 }
156
157 static void tar_import_report_progress(TarImport *i) {
158 unsigned percent;
159 assert(i);
160
161 /* We have no size information, unless the source is a regular file */
162 if (!S_ISREG(i->st.st_mode))
163 return;
164
165 if (i->written_compressed >= (uint64_t) i->st.st_size)
166 percent = 100;
167 else
168 percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size);
169
170 if (percent == i->last_percent)
171 return;
172
173 if (!ratelimit_test(&i->progress_rate_limit))
174 return;
175
176 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
177 log_info("Imported %u%%.", percent);
178
179 i->last_percent = percent;
180 }
181
182 static int tar_import_finish(TarImport *i) {
183 int r;
184
185 assert(i);
186 assert(i->tar_fd >= 0);
187 assert(i->temp_path);
188 assert(i->final_path);
189
190 i->tar_fd = safe_close(i->tar_fd);
191
192 if (i->tar_pid > 0) {
193 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
194 i->tar_pid = 0;
195 if (r < 0)
196 return r;
197 }
198
199 if (i->read_only) {
200 r = import_make_read_only(i->temp_path);
201 if (r < 0)
202 return r;
203 }
204
205 if (i->force_local)
206 (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
207
208 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
209 if (r < 0)
210 return log_error_errno(r, "Failed to move image into place: %m");
211
212 i->temp_path = mfree(i->temp_path);
213
214 return 0;
215 }
216
217 static int tar_import_fork_tar(TarImport *i) {
218 int r;
219
220 assert(i);
221
222 assert(!i->final_path);
223 assert(!i->temp_path);
224 assert(i->tar_fd < 0);
225
226 i->final_path = strjoin(i->image_root, "/", i->local);
227 if (!i->final_path)
228 return log_oom();
229
230 r = tempfn_random(i->final_path, NULL, &i->temp_path);
231 if (r < 0)
232 return log_oom();
233
234 (void) mkdir_parents_label(i->temp_path, 0700);
235
236 r = btrfs_subvol_make(i->temp_path);
237 if (r == -ENOTTY) {
238 if (mkdir(i->temp_path, 0755) < 0)
239 return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
240 } else if (r < 0)
241 return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path);
242 else
243 (void) import_assign_pool_quota_and_warn(i->temp_path);
244
245 i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
246 if (i->tar_fd < 0)
247 return i->tar_fd;
248
249 return 0;
250 }
251
252 static int tar_import_write(const void *p, size_t sz, void *userdata) {
253 TarImport *i = userdata;
254 int r;
255
256 if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) {
257 i->written_since_last_grow = 0;
258 grow_machine_directory();
259 }
260
261 r = loop_write(i->tar_fd, p, sz, false);
262 if (r < 0)
263 return r;
264
265 i->written_uncompressed += sz;
266 i->written_since_last_grow += sz;
267
268 return 0;
269 }
270
271 static int tar_import_process(TarImport *i) {
272 ssize_t l;
273 int r;
274
275 assert(i);
276 assert(i->buffer_size < sizeof(i->buffer));
277
278 l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size);
279 if (l < 0) {
280 if (errno == EAGAIN)
281 return 0;
282
283 r = log_error_errno(errno, "Failed to read input file: %m");
284 goto finish;
285 }
286 if (l == 0) {
287 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
288 log_error("Premature end of file.");
289 r = -EIO;
290 goto finish;
291 }
292
293 r = tar_import_finish(i);
294 goto finish;
295 }
296
297 i->buffer_size += l;
298
299 if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) {
300 r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size);
301 if (r < 0) {
302 log_error_errno(r, "Failed to detect file compression: %m");
303 goto finish;
304 }
305 if (r == 0) /* Need more data */
306 return 0;
307
308 r = tar_import_fork_tar(i);
309 if (r < 0)
310 goto finish;
311 }
312
313 r = import_uncompress(&i->compress, i->buffer, i->buffer_size, tar_import_write, i);
314 if (r < 0) {
315 log_error_errno(r, "Failed to decode and write: %m");
316 goto finish;
317 }
318
319 i->written_compressed += i->buffer_size;
320 i->buffer_size = 0;
321
322 tar_import_report_progress(i);
323
324 return 0;
325
326 finish:
327 if (i->on_finished)
328 i->on_finished(i, r, i->userdata);
329 else
330 sd_event_exit(i->event, r);
331
332 return 0;
333 }
334
335 static int tar_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
336 TarImport *i = userdata;
337
338 return tar_import_process(i);
339 }
340
341 static int tar_import_on_defer(sd_event_source *s, void *userdata) {
342 TarImport *i = userdata;
343
344 return tar_import_process(i);
345 }
346
347 int tar_import_start(TarImport *i, int fd, const char *local, bool force_local, bool read_only) {
348 int r;
349
350 assert(i);
351 assert(fd >= 0);
352 assert(local);
353
354 if (!machine_name_is_valid(local))
355 return -EINVAL;
356
357 if (i->input_fd >= 0)
358 return -EBUSY;
359
360 r = fd_nonblock(fd, true);
361 if (r < 0)
362 return r;
363
364 r = free_and_strdup(&i->local, local);
365 if (r < 0)
366 return r;
367 i->force_local = force_local;
368 i->read_only = read_only;
369
370 if (fstat(fd, &i->st) < 0)
371 return -errno;
372
373 r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i);
374 if (r == -EPERM) {
375 /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */
376 r = sd_event_add_defer(i->event, &i->input_event_source, tar_import_on_defer, i);
377 if (r < 0)
378 return r;
379
380 r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON);
381 }
382 if (r < 0)
383 return r;
384
385 i->input_fd = fd;
386 return r;
387 }