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