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