]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/export-tar.c
btrfs: missing endian conversion fix
[thirdparty/systemd.git] / src / import / export-tar.c
CommitLineData
587fec42
LP
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 <sys/sendfile.h>
23
24#include "sd-daemon.h"
25#include "util.h"
26#include "ratelimit.h"
27#include "btrfs-util.h"
28#include "import-common.h"
29#include "export-tar.h"
30
31#define COPY_BUFFER_SIZE (16*1024)
32
33struct TarExport {
34 sd_event *event;
35
36 TarExportFinished on_finished;
37 void *userdata;
38
39 char *path;
40 char *temp_path;
41
42 int output_fd;
43 int tar_fd;
44
45 ImportCompress compress;
46
47 sd_event_source *output_event_source;
48
49 void *buffer;
50 size_t buffer_size;
51 size_t buffer_allocated;
52
53 uint64_t written_compressed;
54 uint64_t written_uncompressed;
55
56 pid_t tar_pid;
57
58 struct stat st;
59 uint64_t quota_referenced;
60
61 unsigned last_percent;
62 RateLimit progress_rate_limit;
63
64 bool eof;
65 bool tried_splice;
66};
67
68TarExport *tar_export_unref(TarExport *e) {
69 if (!e)
70 return NULL;
71
72 sd_event_source_unref(e->output_event_source);
73
74 if (e->tar_pid > 1) {
75 (void) kill_and_sigcont(e->tar_pid, SIGKILL);
76 (void) wait_for_terminate(e->tar_pid, NULL);
77 }
78
79 if (e->temp_path) {
d9e2daaf 80 (void) btrfs_subvol_remove(e->temp_path, false);
587fec42
LP
81 free(e->temp_path);
82 }
83
84 import_compress_free(&e->compress);
85
86 sd_event_unref(e->event);
87
88 safe_close(e->tar_fd);
89
90 free(e->buffer);
91 free(e->path);
92 free(e);
93
94 return NULL;
95}
96
97int tar_export_new(
98 TarExport **ret,
99 sd_event *event,
100 TarExportFinished on_finished,
101 void *userdata) {
102
103 _cleanup_(tar_export_unrefp) TarExport *e = NULL;
104 int r;
105
106 assert(ret);
107
108 e = new0(TarExport, 1);
109 if (!e)
110 return -ENOMEM;
111
112 e->output_fd = e->tar_fd = -1;
113 e->on_finished = on_finished;
114 e->userdata = userdata;
115 e->quota_referenced = (uint64_t) -1;
116
117 RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
118 e->last_percent = (unsigned) -1;
119
120 if (event)
121 e->event = sd_event_ref(event);
122 else {
123 r = sd_event_default(&e->event);
124 if (r < 0)
125 return r;
126 }
127
128 *ret = e;
129 e = NULL;
130
131 return 0;
132}
133
134static void tar_export_report_progress(TarExport *e) {
135 unsigned percent;
136 assert(e);
137
138 /* Do we have any quota info? I fnot, we don't know anything about the progress */
139 if (e->quota_referenced == (uint64_t) -1)
140 return;
141
142 if (e->written_uncompressed >= e->quota_referenced)
143 percent = 100;
144 else
145 percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / e->quota_referenced);
146
147 if (percent == e->last_percent)
148 return;
149
150 if (!ratelimit_test(&e->progress_rate_limit))
151 return;
152
153 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
154 log_info("Exported %u%%.", percent);
155
156 e->last_percent = percent;
157}
158
159static int tar_export_process(TarExport *e) {
160 ssize_t l;
161 int r;
162
163 assert(e);
164
165 if (!e->tried_splice && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
166
167 l = splice(e->tar_fd, NULL, e->output_fd, NULL, COPY_BUFFER_SIZE, 0);
168 if (l < 0) {
169 if (errno == EAGAIN)
170 return 0;
171
172 e->tried_splice = true;
173 } else if (l == 0) {
174 r = 0;
175 goto finish;
176 } else {
177 e->written_uncompressed += l;
178 e->written_compressed += l;
179
180 tar_export_report_progress(e);
181
182 return 0;
183 }
184 }
185
186 while (e->buffer_size <= 0) {
187 uint8_t input[COPY_BUFFER_SIZE];
188
189 if (e->eof) {
190 r = 0;
191 goto finish;
192 }
193
194 l = read(e->tar_fd, input, sizeof(input));
195 if (l < 0) {
196 r = log_error_errno(errno, "Failed to read tar file: %m");
197 goto finish;
198 }
199
200 if (l == 0) {
201 e->eof = true;
202 r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated);
203 } else {
204 e->written_uncompressed += l;
205 r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated);
206 }
207 if (r < 0) {
208 r = log_error_errno(r, "Failed to encode: %m");
209 goto finish;
210 }
211 }
212
213 l = write(e->output_fd, e->buffer, e->buffer_size);
214 if (l < 0) {
215 if (errno == EAGAIN)
216 return 0;
217
218 r = log_error_errno(errno, "Failed to write output file: %m");
219 goto finish;
220 }
221
222 assert((size_t) l <= e->buffer_size);
223 memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l);
224 e->buffer_size -= l;
225 e->written_compressed += l;
226
227 tar_export_report_progress(e);
228
229 return 0;
230
231finish:
232 if (e->on_finished)
233 e->on_finished(e, r, e->userdata);
234 else
235 sd_event_exit(e->event, r);
236
237 return 0;
238}
239
240static int tar_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
241 TarExport *i = userdata;
242
243 return tar_export_process(i);
244}
245
246static int tar_export_on_defer(sd_event_source *s, void *userdata) {
247 TarExport *i = userdata;
248
249 return tar_export_process(i);
250}
251
252int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) {
253 _cleanup_close_ int sfd = -1;
254 int r;
255
256 assert(e);
257 assert(path);
258 assert(fd >= 0);
259 assert(compress < _IMPORT_COMPRESS_TYPE_MAX);
260 assert(compress != IMPORT_COMPRESS_UNKNOWN);
261
262 if (e->output_fd >= 0)
263 return -EBUSY;
264
265 sfd = open(path, O_DIRECTORY|O_RDONLY|O_NOCTTY|O_CLOEXEC);
266 if (sfd < 0)
267 return -errno;
268
269 if (fstat(sfd, &e->st) < 0)
270 return -errno;
271
272 r = fd_nonblock(fd, true);
273 if (r < 0)
274 return r;
275
276 r = free_and_strdup(&e->path, path);
277 if (r < 0)
278 return r;
279
280 e->quota_referenced = (uint64_t) -1;
281
282 if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */
283 BtrfsQuotaInfo q;
284
285 r = btrfs_subvol_get_quota_fd(sfd, &q);
286 if (r >= 0)
cb81cd80 287 e->quota_referenced = q.referenced;
587fec42
LP
288
289 free(e->temp_path);
290 e->temp_path = NULL;
291
292 r = tempfn_random(path, &e->temp_path);
293 if (r < 0)
294 return r;
295
296 /* Let's try to make a snapshot, if we can, so that the export is atomic */
e9bc1871 297 r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY);
587fec42
LP
298 if (r < 0) {
299 log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path);
300 free(e->temp_path);
301 e->temp_path = NULL;
302 }
303 }
304
305 r = import_compress_init(&e->compress, compress);
306 if (r < 0)
307 return r;
308
309 r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, tar_export_on_output, e);
310 if (r == -EPERM) {
311 r = sd_event_add_defer(e->event, &e->output_event_source, tar_export_on_defer, e);
312 if (r < 0)
313 return r;
314
315 r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON);
316 }
317 if (r < 0)
318 return r;
319
320 e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid);
321 if (e->tar_fd < 0) {
322 e->output_event_source = sd_event_source_unref(e->output_event_source);
323 return e->tar_fd;
324 }
325
326 e->output_fd = fd;
327 return r;
328}