Branch data Line data Source code
1 : : #include <unistd.h>
2 : : #include <sys/types.h>
3 : : #include <sys/stat.h>
4 : : #include <fcntl.h>
5 : : #include <stdlib.h>
6 : : #include <sys/mman.h>
7 : :
8 : : #include "crtools.h"
9 : : #include "image.h"
10 : : #include "files.h"
11 : : #include "pipes.h"
12 : : #include "util-net.h"
13 : :
14 : : #include "protobuf.h"
15 : : #include "protobuf/pipe.pb-c.h"
16 : : #include "protobuf/pipe-data.pb-c.h"
17 : :
18 : : /*
19 : : * The sequence of objects which should be restored:
20 : : * pipe -> files struct-s -> fd-s.
21 : : * pipe_entry describes pipe's file structs-s.
22 : : * A pipe doesn't have own properties, so it has no object.
23 : : */
24 : :
25 : : struct pipe_info {
26 : : PipeEntry *pe;
27 : : struct list_head pipe_list; /* All pipe_info with the same pipe_id
28 : : * This is pure circular list without head */
29 : : struct list_head list; /* list head for fdinfo_list_entry-s */
30 : : struct file_desc d;
31 : : int create;
32 : : };
33 : :
34 : : static LIST_HEAD(pipes);
35 : :
36 : 452 : static void show_saved_pipe_fds(struct pipe_info *pi)
37 : : {
38 : : struct fdinfo_list_entry *fle;
39 : :
40 : 452 : pr_info(" `- ID %p %#xpn", pi, pi->pe->id);
41 [ + + ]: 1373 : list_for_each_entry(fle, &pi->d.fd_info_head, desc_list)
42 : 921 : pr_info(" `- FD %d pid %d\n", fle->fe->fd, fle->pid);
43 : 452 : }
44 : :
45 : 24 : static int pipe_data_read(int fd, struct pipe_data_rst *r)
46 : : {
47 : : /*
48 : : * We potentially allocate more memory than required for data,
49 : : * but this is OK. Look at restore_pipe_data -- it vmsplice-s
50 : : * this into the kernel with F_GIFT flag (since some time it
51 : : * works on non-aligned data), thus just giving this page to
52 : : * pipe buffer. And since kernel allocates pipe buffers in pages
53 : : * anyway we don't increase memory consumption :)
54 : : */
55 : :
56 : 24 : r->data = mmap(NULL, r->pde->bytes, PROT_READ | PROT_WRITE,
57 : : MAP_SHARED | MAP_ANON, 0, 0);
58 [ - + ]: 24 : if (r->data == MAP_FAILED) {
59 : 0 : pr_perror("Can't map mem for pipe buffers");
60 : : return -1;
61 : : }
62 : :
63 : 24 : return read_img_buf(fd, r->data, r->pde->bytes);
64 : : }
65 : :
66 : 710 : int collect_pipe_data(int img_type, struct pipe_data_rst **hash)
67 : : {
68 : : int fd, ret;
69 : 710 : struct pipe_data_rst *r = NULL;
70 : :
71 : 710 : fd = open_image_ro(img_type);
72 [ + - ]: 710 : if (fd < 0)
73 : : return -1;
74 : :
75 : : while (1) {
76 : 734 : ret = -1;
77 [ - + ]: 734 : r = xmalloc(sizeof(*r));
78 [ + - ]: 734 : if (!r)
79 : : break;
80 : :
81 : 734 : ret = pb_read_one_eof(fd, &r->pde, PB_PIPES_DATA);
82 [ + + ]: 734 : if (ret <= 0)
83 : : break;
84 : :
85 : 24 : ret = pipe_data_read(fd, r);
86 [ + - ]: 24 : if (ret < 0)
87 : : break;
88 : :
89 : 24 : ret = r->pde->pipe_id & PIPE_DATA_HASH_MASK;
90 : 24 : r->next = hash[ret];
91 : 24 : hash[ret] = r;
92 : :
93 : 24 : pr_info("Collected pipe data for %#x (chain %u)\n",
94 : : r->pde->pipe_id, ret);
95 : 24 : }
96 : :
97 [ + - ][ - + ]: 710 : if (r && r->pde)
98 : 0 : pipe_data_entry__free_unpacked(r->pde, NULL);
99 [ + - ]: 710 : xfree(r);
100 : :
101 : 710 : close(fd);
102 : 710 : return ret;
103 : : }
104 : :
105 : : /* Choose who will restore a pipe. */
106 : 355 : void mark_pipe_master(void)
107 : : {
108 : 355 : LIST_HEAD(head);
109 : :
110 : 355 : pr_info("Pipes:\n");
111 : :
112 : : while (1) {
113 : : struct fdinfo_list_entry *fle;
114 : : struct pipe_info *pi, *pic, *p;
115 : :
116 [ + + ]: 607 : if (list_empty(&pipes))
117 : : break;
118 : :
119 : 252 : pi = list_first_entry(&pipes, struct pipe_info, list);
120 : 252 : list_move(&pi->list, &head);
121 : :
122 : 252 : pr_info(" `- PIPE ID %#x\n", pi->pe->pipe_id);
123 : 252 : show_saved_pipe_fds(pi);
124 : :
125 : 252 : fle = file_master(&pi->d);
126 : 252 : p = pi;
127 : :
128 [ + + ]: 452 : list_for_each_entry(pic, &pi->pipe_list, pipe_list) {
129 : : struct fdinfo_list_entry *f;
130 : :
131 : 200 : list_move(&pic->list, &head);
132 : 200 : f = file_master(&pic->d);
133 [ - + ]: 200 : if (fdinfo_rst_prio(f, fle)) {
134 : 0 : p = pic;
135 : 0 : fle = f;
136 : : }
137 : :
138 : 200 : show_saved_pipe_fds(pic);
139 : : }
140 : 252 : p->create = 1;
141 : 252 : pr_info(" by %#x\n", p->pe->id);
142 : 252 : }
143 : :
144 : : list_splice(&head, &pipes);
145 : 355 : }
146 : :
147 : : static struct pipe_data_rst *pd_hash_pipes[PIPE_DATA_HASH_SIZE];
148 : :
149 : 75 : int restore_pipe_data(int img_type, int pfd, u32 id, struct pipe_data_rst **hash)
150 : : {
151 : : int ret;
152 : : struct pipe_data_rst *pd;
153 : : struct iovec iov;
154 : :
155 [ + + ]: 75 : for (pd = hash[id & PIPE_DATA_HASH_MASK]; pd != NULL; pd = pd->next)
156 [ - + ]: 12 : if (pd->pde->pipe_id == id)
157 : : break;
158 : :
159 [ + + ]: 75 : if (!pd) { /* no data for this pipe */
160 : 63 : pr_info("No data for pipe %#x\n", id);
161 : : return 0;
162 : : }
163 : :
164 [ - + ]: 12 : if (!pd->data) {
165 : 0 : pr_err("Double data restore occurred on %#x\n", id);
166 : : return -1;
167 : : }
168 : :
169 : 12 : iov.iov_base = pd->data;
170 : 12 : iov.iov_len = pd->pde->bytes;
171 : :
172 [ + + ]: 24 : while (iov.iov_len > 0) {
173 : 12 : ret = vmsplice(pfd, &iov, 1, SPLICE_F_GIFT | SPLICE_F_NONBLOCK);
174 [ - + ]: 12 : if (ret < 0) {
175 : 0 : pr_perror("%#x: Error splicing data", id);
176 : 0 : goto err;
177 : : }
178 : :
179 [ + - ][ - + ]: 12 : if (ret == 0 || ret > iov.iov_len /* sanity */) {
180 : 0 : pr_err("%#x: Wanted to restore %lu bytes, but got %d\n", id,
181 : : iov.iov_len, ret);
182 : 0 : ret = -1;
183 : 0 : goto err;
184 : : }
185 : :
186 : 12 : iov.iov_base += ret;
187 : 12 : iov.iov_len -= ret;
188 : : }
189 : :
190 : : /*
191 : : * 3 reasons for killing the buffer from our address space:
192 : : *
193 : : * 1. We gifted the pages to the kernel to optimize memory usage, thus
194 : : * accidental memory corruption can change the pipe buffer.
195 : : * 2. This will make the vmas restoration a bit faster due to less self
196 : : * mappings to be unmapped.
197 : : * 3. We can catch bugs with double pipe data restore.
198 : : */
199 : :
200 : 12 : munmap(pd->data, pd->pde->bytes);
201 : 12 : pd->data = NULL;
202 : 75 : ret = 0;
203 : : err:
204 : : return ret;
205 : : }
206 : :
207 : 61 : static int recv_pipe_fd(struct pipe_info *pi)
208 : : {
209 : : struct fdinfo_list_entry *fle;
210 : : char path[32];
211 : : int tmp, fd;
212 : :
213 : 61 : fle = file_master(&pi->d);
214 : 61 : fd = fle->fe->fd;
215 : :
216 : 61 : pr_info("\tWaiting fd for %d\n", fd);
217 : :
218 : 61 : tmp = recv_fd(fd);
219 [ - + ]: 61 : if (tmp < 0) {
220 : 0 : pr_err("Can't get fd %d\n", tmp);
221 : : return -1;
222 : : }
223 : 61 : close(fd);
224 : :
225 : 61 : snprintf(path, sizeof(path), "/proc/self/fd/%d", tmp);
226 : 61 : fd = open(path, pi->pe->flags);
227 : 61 : close(tmp);
228 : :
229 [ + - ]: 61 : if (fd >= 0) {
230 [ - + ]: 61 : if (restore_fown(fd, pi->pe->fown)) {
231 : 61 : close(fd);
232 : : return -1;
233 : : }
234 : : }
235 : :
236 : : return fd;
237 : : }
238 : :
239 : 126 : static int open_pipe(struct file_desc *d)
240 : : {
241 : : struct pipe_info *pi, *p;
242 : : int ret, tmp;
243 : : int pfd[2];
244 : : int sock;
245 : :
246 : 126 : pi = container_of(d, struct pipe_info, d);
247 : :
248 : 126 : pr_info("\t\tCreating pipe pipe_id=%#x id=%#x\n", pi->pe->pipe_id, pi->pe->id);
249 : :
250 [ + + ]: 126 : if (!pi->create)
251 : 61 : return recv_pipe_fd(pi);
252 : :
253 [ - + ]: 65 : if (pipe(pfd) < 0) {
254 : 0 : pr_perror("Can't create pipe");
255 : : return -1;
256 : : }
257 : :
258 : 65 : ret = restore_pipe_data(CR_FD_PIPES_DATA, pfd[1],
259 : 65 : pi->pe->pipe_id, pd_hash_pipes);
260 [ + - ]: 65 : if (ret)
261 : : return -1;
262 : :
263 : 65 : sock = socket(PF_UNIX, SOCK_DGRAM, 0);
264 [ - + ]: 65 : if (sock < 0) {
265 : 0 : pr_perror("Can't create socket");
266 : : return -1;
267 : : }
268 : :
269 [ + + ]: 126 : list_for_each_entry(p, &pi->pipe_list, pipe_list) {
270 : : struct fdinfo_list_entry *fle;
271 : : int fd;
272 : :
273 : 61 : fle = file_master(&p->d);
274 : 61 : fd = pfd[p->pe->flags & O_WRONLY];
275 : :
276 [ - + ]: 61 : if (send_fd_to_peer(fd, fle, sock)) {
277 : 0 : pr_perror("Can't send file descriptor");
278 : : return -1;
279 : : }
280 : : }
281 : :
282 : 65 : close(sock);
283 : :
284 : 65 : close(pfd[!(pi->pe->flags & O_WRONLY)]);
285 : 65 : tmp = pfd[pi->pe->flags & O_WRONLY];
286 : :
287 [ + - ]: 126 : if (rst_file_params(tmp, pi->pe->fown, pi->pe->flags))
288 : : return -1;
289 : :
290 : : return tmp;
291 : : }
292 : :
293 : 126 : static int want_transport(FdinfoEntry *fe, struct file_desc *d)
294 : : {
295 : : struct pipe_info *pi;
296 : :
297 : 126 : pi = container_of(d, struct pipe_info, d);
298 : 126 : return !pi->create;
299 : : }
300 : :
301 : : static struct file_desc_ops pipe_desc_ops = {
302 : : .type = FD_TYPES__PIPE,
303 : : .open = open_pipe,
304 : : .want_transport = want_transport,
305 : : };
306 : :
307 : 452 : static int collect_one_pipe(void *o, ProtobufCMessage *base)
308 : : {
309 : 452 : struct pipe_info *pi = o, *tmp;
310 : :
311 : 452 : pi->pe = pb_msg(base, PipeEntry);
312 : :
313 : 452 : pi->create = 0;
314 : 452 : pr_info("Collected pipe entry ID %#x PIPE ID %#x\n",
315 : : pi->pe->id, pi->pe->pipe_id);
316 : :
317 : 452 : file_desc_add(&pi->d, pi->pe->id, &pipe_desc_ops);
318 : :
319 [ + + ]: 1558 : list_for_each_entry(tmp, &pipes, list)
320 [ + + ]: 1306 : if (pi->pe->pipe_id == tmp->pe->pipe_id)
321 : : break;
322 : :
323 [ + + ]: 452 : if (&tmp->list == &pipes)
324 : 252 : INIT_LIST_HEAD(&pi->pipe_list);
325 : : else
326 : 200 : list_add(&pi->pipe_list, &tmp->pipe_list);
327 : :
328 : 452 : list_add_tail(&pi->list, &pipes);
329 : :
330 : 452 : return 0;
331 : : }
332 : :
333 : 355 : int collect_pipes(void)
334 : : {
335 : : int ret;
336 : :
337 : 355 : ret = collect_image(CR_FD_PIPES, PB_PIPES,
338 : : sizeof(struct pipe_info), collect_one_pipe);
339 [ + - ]: 355 : if (!ret)
340 : 355 : ret = collect_pipe_data(CR_FD_PIPES_DATA, pd_hash_pipes);
341 : :
342 : 355 : return ret;
343 : : }
344 : :
345 : 140 : int dump_one_pipe_data(struct pipe_data_dump *pd, int lfd, const struct fd_parms *p)
346 : : {
347 : : int img;
348 : : int pipe_size, i, bytes;
349 : : int steal_pipe[2];
350 : 140 : int ret = -1;
351 : :
352 [ + + ]: 140 : if (p->flags & O_WRONLY)
353 : : return 0;
354 : :
355 : : /* Maybe we've dumped it already */
356 [ + + ]: 153 : for (i = 0; i < pd->nr; i++) {
357 [ + + ]: 78 : if (pd->ids[i] == pipe_id(p))
358 : : return 0;
359 : : }
360 : :
361 : 75 : pr_info("Dumping data from pipe %#x fd %d\n", pipe_id(p), lfd);
362 : :
363 [ - + ]: 75 : if (pd->nr >= NR_PIPES_WITH_DATA) {
364 : 0 : pr_err("OOM storing pipe\n");
365 : : return -1;
366 : : }
367 : :
368 : 75 : img = fdset_fd(glob_fdset, pd->img_type);
369 : 225 : pd->ids[pd->nr++] = pipe_id(p);
370 : :
371 : 75 : pipe_size = fcntl(lfd, F_GETPIPE_SZ);
372 [ - + ]: 75 : if (pipe_size < 0) {
373 : 0 : pr_err("Can't obtain piped data size\n");
374 : 0 : goto err;
375 : : }
376 : :
377 [ - + ]: 75 : if (pipe(steal_pipe) < 0) {
378 : 0 : pr_perror("Can't create pipe for stealing data");
379 : 0 : goto err;
380 : : }
381 : :
382 : 75 : bytes = tee(lfd, steal_pipe[1], pipe_size, SPLICE_F_NONBLOCK);
383 [ + + ]: 75 : if (bytes > 0) {
384 : 12 : PipeDataEntry pde = PIPE_DATA_ENTRY__INIT;
385 : : int wrote;
386 : :
387 : 24 : pde.pipe_id = pipe_id(p);
388 : 12 : pde.bytes = bytes;
389 : :
390 [ + - ]: 12 : if (pb_write_one(img, &pde, PB_PIPES_DATA))
391 : : goto err_close;
392 : :
393 : 12 : wrote = splice(steal_pipe[0], NULL, img, NULL, bytes, 0);
394 [ - + ]: 12 : if (wrote < 0) {
395 : 0 : pr_perror("Can't push pipe data");
396 : : goto err_close;
397 [ - + ]: 12 : } else if (wrote != bytes) {
398 : 12 : pr_err("%#x: Wanted to write %d bytes, but wrote %d\n",
399 : : pipe_id(p), bytes, wrote);
400 : : goto err_close;
401 : : }
402 [ + + ]: 63 : } else if (bytes < 0) {
403 [ - + ]: 55 : if (errno != EAGAIN) {
404 : 0 : pr_perror("Can't pick pipe data");
405 : 0 : goto err_close;
406 : : }
407 : : }
408 : :
409 : : ret = 0;
410 : :
411 : : err_close:
412 : 75 : close(steal_pipe[0]);
413 : 140 : close(steal_pipe[1]);
414 : : err:
415 : : return ret;
416 : : }
417 : :
418 : : static struct pipe_data_dump pd_pipes = { .img_type = CR_FD_PIPES_DATA, };
419 : :
420 : 126 : static int dump_one_pipe(int lfd, u32 id, const struct fd_parms *p)
421 : : {
422 : 126 : PipeEntry pe = PIPE_ENTRY__INIT;
423 : :
424 : 126 : pr_info("Dumping pipe %d with id %#x pipe_id %#x\n",
425 : : lfd, id, pipe_id(p));
426 : :
427 : 126 : pe.id = id;
428 : 252 : pe.pipe_id = pipe_id(p);
429 : 126 : pe.flags = p->flags;
430 : 126 : pe.fown = (FownEntry *)&p->fown;
431 : :
432 [ + - ]: 126 : if (pb_write_one(fdset_fd(glob_fdset, CR_FD_PIPES), &pe, PB_PIPES))
433 : : return -1;
434 : :
435 : 126 : return dump_one_pipe_data(&pd_pipes, lfd, p);
436 : : }
437 : :
438 : : static const struct fdtype_ops pipe_ops = {
439 : : .type = FD_TYPES__PIPE,
440 : : .dump = dump_one_pipe,
441 : : };
442 : :
443 : 238 : int dump_pipe(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset)
444 : : {
445 : 238 : return do_dump_gen_file(p, lfd, &pipe_ops, cr_fdset);
446 : 916 : }
|