xref: /openbsd-src/usr.bin/tmux/file.c (revision 0c0c2584134dbfad42dbd43ce22a56b1e92057f6)
1 /* $OpenBSD: file.c,v 1.8 2021/02/11 08:28:45 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/uio.h>
22 
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <imsg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "tmux.h"
32 
33 /*
34  * IPC file handling. Both client and server use the same data structures
35  * (client_file and client_files) to store list of active files. Most functions
36  * are for use either in client or server but not both.
37  */
38 
39 static int	file_next_stream = 3;
40 
41 RB_GENERATE(client_files, client_file, entry, file_cmp);
42 
43 /* Get path for file, either as given or from working directory. */
44 static char *
45 file_get_path(struct client *c, const char *file)
46 {
47 	char	*path;
48 
49 	if (*file == '/')
50 		path = xstrdup(file);
51 	else
52 		xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
53 	return (path);
54 }
55 
56 /* Tree comparison function. */
57 int
58 file_cmp(struct client_file *cf1, struct client_file *cf2)
59 {
60 	if (cf1->stream < cf2->stream)
61 		return (-1);
62 	if (cf1->stream > cf2->stream)
63 		return (1);
64 	return (0);
65 }
66 
67 /*
68  * Create a file object in the client process - the peer is the server to send
69  * messages to. Check callback is fired when the file is finished with so the
70  * process can decide if it needs to exit (if it is waiting for files to
71  * flush).
72  */
73 struct client_file *
74 file_create_with_peer(struct tmuxpeer *peer, struct client_files *files,
75     int stream, client_file_cb cb, void *cbdata)
76 {
77 	struct client_file	*cf;
78 
79 	cf = xcalloc(1, sizeof *cf);
80 	cf->c = NULL;
81 	cf->references = 1;
82 	cf->stream = stream;
83 
84 	cf->buffer = evbuffer_new();
85 	if (cf->buffer == NULL)
86 		fatalx("out of memory");
87 
88 	cf->cb = cb;
89 	cf->data = cbdata;
90 
91 	cf->peer = peer;
92 	cf->tree = files;
93 	RB_INSERT(client_files, files, cf);
94 
95 	return (cf);
96 }
97 
98 /* Create a file object in the server, communicating with the given client. */
99 struct client_file *
100 file_create_with_client(struct client *c, int stream, client_file_cb cb,
101     void *cbdata)
102 {
103 	struct client_file	*cf;
104 
105 	if (c != NULL && (c->flags & CLIENT_ATTACHED))
106 		c = NULL;
107 
108 	cf = xcalloc(1, sizeof *cf);
109 	cf->c = c;
110 	cf->references = 1;
111 	cf->stream = stream;
112 
113 	cf->buffer = evbuffer_new();
114 	if (cf->buffer == NULL)
115 		fatalx("out of memory");
116 
117 	cf->cb = cb;
118 	cf->data = cbdata;
119 
120 	if (cf->c != NULL) {
121 		cf->peer = cf->c->peer;
122 		cf->tree = &cf->c->files;
123 		RB_INSERT(client_files, &cf->c->files, cf);
124 		cf->c->references++;
125 	}
126 
127 	return (cf);
128 }
129 
130 /* Free a file. */
131 void
132 file_free(struct client_file *cf)
133 {
134 	if (--cf->references != 0)
135 		return;
136 
137 	evbuffer_free(cf->buffer);
138 	free(cf->path);
139 
140 	if (cf->tree != NULL)
141 		RB_REMOVE(client_files, cf->tree, cf);
142 	if (cf->c != NULL)
143 		server_client_unref(cf->c);
144 
145 	free(cf);
146 }
147 
148 /* Event to fire the done callback. */
149 static void
150 file_fire_done_cb(__unused int fd, __unused short events, void *arg)
151 {
152 	struct client_file	*cf = arg;
153 	struct client		*c = cf->c;
154 
155 	if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
156 		cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
157 	file_free(cf);
158 }
159 
160 /* Add an event to fire the done callback (used by the server). */
161 void
162 file_fire_done(struct client_file *cf)
163 {
164 	event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
165 }
166 
167 /* Fire the read callback. */
168 void
169 file_fire_read(struct client_file *cf)
170 {
171 	if (cf->cb != NULL)
172 		cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data);
173 }
174 
175 /* Can this file be printed to? */
176 int
177 file_can_print(struct client *c)
178 {
179 	if (c == NULL)
180 		return (0);
181 	if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
182 		return (0);
183 	return (1);
184 }
185 
186 /* Print a message to a file. */
187 void
188 file_print(struct client *c, const char *fmt, ...)
189 {
190 	va_list	ap;
191 
192 	va_start(ap, fmt);
193 	file_vprint(c, fmt, ap);
194 	va_end(ap);
195 }
196 
197 /* Print a message to a file. */
198 void
199 file_vprint(struct client *c, const char *fmt, va_list ap)
200 {
201 	struct client_file	 find, *cf;
202 	struct msg_write_open	 msg;
203 
204 	if (!file_can_print(c))
205 		return;
206 
207 	find.stream = 1;
208 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
209 		cf = file_create_with_client(c, 1, NULL, NULL);
210 		cf->path = xstrdup("-");
211 
212 		evbuffer_add_vprintf(cf->buffer, fmt, ap);
213 
214 		msg.stream = 1;
215 		msg.fd = STDOUT_FILENO;
216 		msg.flags = 0;
217 		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
218 	} else {
219 		evbuffer_add_vprintf(cf->buffer, fmt, ap);
220 		file_push(cf);
221 	}
222 }
223 
224 /* Print a buffer to a file. */
225 void
226 file_print_buffer(struct client *c, void *data, size_t size)
227 {
228 	struct client_file	 find, *cf;
229 	struct msg_write_open	 msg;
230 
231 	if (!file_can_print(c))
232 		return;
233 
234 	find.stream = 1;
235 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
236 		cf = file_create_with_client(c, 1, NULL, NULL);
237 		cf->path = xstrdup("-");
238 
239 		evbuffer_add(cf->buffer, data, size);
240 
241 		msg.stream = 1;
242 		msg.fd = STDOUT_FILENO;
243 		msg.flags = 0;
244 		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
245 	} else {
246 		evbuffer_add(cf->buffer, data, size);
247 		file_push(cf);
248 	}
249 }
250 
251 /* Report an error to a file. */
252 void
253 file_error(struct client *c, const char *fmt, ...)
254 {
255 	struct client_file	 find, *cf;
256 	struct msg_write_open	 msg;
257 	va_list			 ap;
258 
259 	if (!file_can_print(c))
260 		return;
261 
262 	va_start(ap, fmt);
263 
264 	find.stream = 2;
265 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
266 		cf = file_create_with_client(c, 2, NULL, NULL);
267 		cf->path = xstrdup("-");
268 
269 		evbuffer_add_vprintf(cf->buffer, fmt, ap);
270 
271 		msg.stream = 2;
272 		msg.fd = STDERR_FILENO;
273 		msg.flags = 0;
274 		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
275 	} else {
276 		evbuffer_add_vprintf(cf->buffer, fmt, ap);
277 		file_push(cf);
278 	}
279 
280 	va_end(ap);
281 }
282 
283 /* Write data to a file. */
284 void
285 file_write(struct client *c, const char *path, int flags, const void *bdata,
286     size_t bsize, client_file_cb cb, void *cbdata)
287 {
288 	struct client_file	*cf;
289 	struct msg_write_open	*msg;
290 	size_t			 msglen;
291 	int			 fd = -1;
292 	u_int			 stream = file_next_stream++;
293 	FILE			*f;
294 	const char		*mode;
295 
296 	if (strcmp(path, "-") == 0) {
297 		cf = file_create_with_client(c, stream, cb, cbdata);
298 		cf->path = xstrdup("-");
299 
300 		fd = STDOUT_FILENO;
301 		if (c == NULL ||
302 		    (c->flags & CLIENT_ATTACHED) ||
303 		    (c->flags & CLIENT_CONTROL)) {
304 			cf->error = EBADF;
305 			goto done;
306 		}
307 		goto skip;
308 	}
309 
310 	cf = file_create_with_client(c, stream, cb, cbdata);
311 	cf->path = file_get_path(c, path);
312 
313 	if (c == NULL || c->flags & CLIENT_ATTACHED) {
314 		if (flags & O_APPEND)
315 			mode = "ab";
316 		else
317 			mode = "wb";
318 		f = fopen(cf->path, mode);
319 		if (f == NULL) {
320 			cf->error = errno;
321 			goto done;
322 		}
323 		if (fwrite(bdata, 1, bsize, f) != bsize) {
324 			fclose(f);
325 			cf->error = EIO;
326 			goto done;
327 		}
328 		fclose(f);
329 		goto done;
330 	}
331 
332 skip:
333 	evbuffer_add(cf->buffer, bdata, bsize);
334 
335 	msglen = strlen(cf->path) + 1 + sizeof *msg;
336 	if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
337 		cf->error = E2BIG;
338 		goto done;
339 	}
340 	msg = xmalloc(msglen);
341 	msg->stream = cf->stream;
342 	msg->fd = fd;
343 	msg->flags = flags;
344 	memcpy(msg + 1, cf->path, msglen - sizeof *msg);
345 	if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
346 		free(msg);
347 		cf->error = EINVAL;
348 		goto done;
349 	}
350 	free(msg);
351 	return;
352 
353 done:
354 	file_fire_done(cf);
355 }
356 
357 /* Read a file. */
358 void
359 file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
360 {
361 	struct client_file	*cf;
362 	struct msg_read_open	*msg;
363 	size_t			 msglen;
364 	int			 fd = -1;
365 	u_int			 stream = file_next_stream++;
366 	FILE			*f;
367 	size_t			 size;
368 	char			 buffer[BUFSIZ];
369 
370 	if (strcmp(path, "-") == 0) {
371 		cf = file_create_with_client(c, stream, cb, cbdata);
372 		cf->path = xstrdup("-");
373 
374 		fd = STDIN_FILENO;
375 		if (c == NULL ||
376 		    (c->flags & CLIENT_ATTACHED) ||
377 		    (c->flags & CLIENT_CONTROL)) {
378 			cf->error = EBADF;
379 			goto done;
380 		}
381 		goto skip;
382 	}
383 
384 	cf = file_create_with_client(c, stream, cb, cbdata);
385 	cf->path = file_get_path(c, path);
386 
387 	if (c == NULL || c->flags & CLIENT_ATTACHED) {
388 		f = fopen(cf->path, "rb");
389 		if (f == NULL) {
390 			cf->error = errno;
391 			goto done;
392 		}
393 		for (;;) {
394 			size = fread(buffer, 1, sizeof buffer, f);
395 			if (evbuffer_add(cf->buffer, buffer, size) != 0) {
396 				cf->error = ENOMEM;
397 				goto done;
398 			}
399 			if (size != sizeof buffer)
400 				break;
401 		}
402 		if (ferror(f)) {
403 			cf->error = EIO;
404 			goto done;
405 		}
406 		fclose(f);
407 		goto done;
408 	}
409 
410 skip:
411 	msglen = strlen(cf->path) + 1 + sizeof *msg;
412 	if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
413 		cf->error = E2BIG;
414 		goto done;
415 	}
416 	msg = xmalloc(msglen);
417 	msg->stream = cf->stream;
418 	msg->fd = fd;
419 	memcpy(msg + 1, cf->path, msglen - sizeof *msg);
420 	if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
421 		free(msg);
422 		cf->error = EINVAL;
423 		goto done;
424 	}
425 	free(msg);
426 	return;
427 
428 done:
429 	file_fire_done(cf);
430 }
431 
432 /* Push event, fired if there is more writing to be done. */
433 static void
434 file_push_cb(__unused int fd, __unused short events, void *arg)
435 {
436 	struct client_file	*cf = arg;
437 
438 	if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
439 		file_push(cf);
440 	file_free(cf);
441 }
442 
443 /* Push uwritten data to the client for a file, if it will accept it. */
444 void
445 file_push(struct client_file *cf)
446 {
447 	struct msg_write_data	*msg;
448 	size_t			 msglen, sent, left;
449 	struct msg_write_close	 close;
450 
451 	msg = xmalloc(sizeof *msg);
452 	left = EVBUFFER_LENGTH(cf->buffer);
453 	while (left != 0) {
454 		sent = left;
455 		if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
456 			sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
457 
458 		msglen = (sizeof *msg) + sent;
459 		msg = xrealloc(msg, msglen);
460 		msg->stream = cf->stream;
461 		memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
462 		if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
463 			break;
464 		evbuffer_drain(cf->buffer, sent);
465 
466 		left = EVBUFFER_LENGTH(cf->buffer);
467 		log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
468 	}
469 	if (left != 0) {
470 		cf->references++;
471 		event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
472 	} else if (cf->stream > 2) {
473 		close.stream = cf->stream;
474 		proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
475 		file_fire_done(cf);
476 	}
477 	free(msg);
478 }
479 
480 /* Client file write error callback. */
481 static void
482 file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
483     void *arg)
484 {
485 	struct client_file	*cf = arg;
486 
487 	log_debug("write error file %d", cf->stream);
488 
489 	if (cf->cb != NULL)
490 		cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
491 
492 	bufferevent_free(cf->event);
493 	cf->event = NULL;
494 
495 	close(cf->fd);
496 	cf->fd = -1;
497 }
498 
499 /* Client file write callback. */
500 static void
501 file_write_callback(__unused struct bufferevent *bev, void *arg)
502 {
503 	struct client_file	*cf = arg;
504 
505 	log_debug("write check file %d", cf->stream);
506 
507 	if (cf->cb != NULL)
508 		cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
509 
510 	if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
511 		bufferevent_free(cf->event);
512 		close(cf->fd);
513 		RB_REMOVE(client_files, cf->tree, cf);
514 		file_free(cf);
515 	}
516 }
517 
518 /* Handle a file write open message (client). */
519 void
520 file_write_open(struct client_files *files, struct tmuxpeer *peer,
521     struct imsg *imsg, int allow_streams, int close_received,
522     client_file_cb cb, void *cbdata)
523 {
524 	struct msg_write_open	*msg = imsg->data;
525 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
526 	const char		*path;
527 	struct msg_write_ready	 reply;
528 	struct client_file	 find, *cf;
529 	const int		 flags = O_NONBLOCK|O_WRONLY|O_CREAT;
530 	int			 error = 0;
531 
532 	if (msglen < sizeof *msg)
533 		fatalx("bad MSG_WRITE_OPEN size");
534 	if (msglen == sizeof *msg)
535 		path = "-";
536 	else
537 		path = (const char *)(msg + 1);
538 	log_debug("open write file %d %s", msg->stream, path);
539 
540 	find.stream = msg->stream;
541 	if ((cf = RB_FIND(client_files, files, &find)) != NULL) {
542 		error = EBADF;
543 		goto reply;
544 	}
545 	cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
546 	if (cf->closed) {
547 		error = EBADF;
548 		goto reply;
549 	}
550 
551 	cf->fd = -1;
552 	if (msg->fd == -1)
553 		cf->fd = open(path, msg->flags|flags, 0644);
554 	else if (allow_streams) {
555 		if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
556 			errno = EBADF;
557 		else {
558 			cf->fd = dup(msg->fd);
559 			if (close_received)
560 				close(msg->fd); /* can only be used once */
561 		}
562 	} else
563 	      errno = EBADF;
564 	if (cf->fd == -1) {
565 		error = errno;
566 		goto reply;
567 	}
568 
569 	cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
570 	    file_write_error_callback, cf);
571 	bufferevent_enable(cf->event, EV_WRITE);
572 	goto reply;
573 
574 reply:
575 	reply.stream = msg->stream;
576 	reply.error = error;
577 	proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
578 }
579 
580 /* Handle a file write data message (client). */
581 void
582 file_write_data(struct client_files *files, struct imsg *imsg)
583 {
584 	struct msg_write_data	*msg = imsg->data;
585 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
586 	struct client_file	 find, *cf;
587 	size_t			 size = msglen - sizeof *msg;
588 
589 	if (msglen < sizeof *msg)
590 		fatalx("bad MSG_WRITE size");
591 	find.stream = msg->stream;
592 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
593 		fatalx("unknown stream number");
594 	log_debug("write %zu to file %d", size, cf->stream);
595 
596 	if (cf->event != NULL)
597 		bufferevent_write(cf->event, msg + 1, size);
598 }
599 
600 /* Handle a file write close message (client). */
601 void
602 file_write_close(struct client_files *files, struct imsg *imsg)
603 {
604 	struct msg_write_close	*msg = imsg->data;
605 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
606 	struct client_file	 find, *cf;
607 
608 	if (msglen != sizeof *msg)
609 		fatalx("bad MSG_WRITE_CLOSE size");
610 	find.stream = msg->stream;
611 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
612 		fatalx("unknown stream number");
613 	log_debug("close file %d", cf->stream);
614 
615 	if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
616 		if (cf->event != NULL)
617 			bufferevent_free(cf->event);
618 		if (cf->fd != -1)
619 			close(cf->fd);
620 		RB_REMOVE(client_files, files, cf);
621 		file_free(cf);
622 	}
623 }
624 
625 /* Client file read error callback. */
626 static void
627 file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
628     void *arg)
629 {
630 	struct client_file	*cf = arg;
631 	struct msg_read_done	 msg;
632 
633 	log_debug("read error file %d", cf->stream);
634 
635 	msg.stream = cf->stream;
636 	msg.error = 0;
637 	proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
638 
639 	bufferevent_free(cf->event);
640 	close(cf->fd);
641 	RB_REMOVE(client_files, cf->tree, cf);
642 	file_free(cf);
643 }
644 
645 /* Client file read callback. */
646 static void
647 file_read_callback(__unused struct bufferevent *bev, void *arg)
648 {
649 	struct client_file	*cf = arg;
650 	void			*bdata;
651 	size_t			 bsize;
652 	struct msg_read_data	*msg;
653 	size_t			 msglen;
654 
655 	msg = xmalloc(sizeof *msg);
656 	for (;;) {
657 		bdata = EVBUFFER_DATA(cf->event->input);
658 		bsize = EVBUFFER_LENGTH(cf->event->input);
659 
660 		if (bsize == 0)
661 			break;
662 		if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
663 			bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
664 		log_debug("read %zu from file %d", bsize, cf->stream);
665 
666 		msglen = (sizeof *msg) + bsize;
667 		msg = xrealloc(msg, msglen);
668 		msg->stream = cf->stream;
669 		memcpy(msg + 1, bdata, bsize);
670 		proc_send(cf->peer, MSG_READ, -1, msg, msglen);
671 
672 		evbuffer_drain(cf->event->input, bsize);
673 	}
674 	free(msg);
675 }
676 
677 /* Handle a file read open message (client). */
678 void
679 file_read_open(struct client_files *files, struct tmuxpeer *peer,
680     struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb,
681     void *cbdata)
682 {
683 	struct msg_read_open	*msg = imsg->data;
684 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
685 	const char		*path;
686 	struct msg_read_done	 reply;
687 	struct client_file	 find, *cf;
688 	const int		 flags = O_NONBLOCK|O_RDONLY;
689 	int			 error;
690 
691 	if (msglen < sizeof *msg)
692 		fatalx("bad MSG_READ_OPEN size");
693 	if (msglen == sizeof *msg)
694 		path = "-";
695 	else
696 		path = (const char *)(msg + 1);
697 	log_debug("open read file %d %s", msg->stream, path);
698 
699 	find.stream = msg->stream;
700 	if ((cf = RB_FIND(client_files, files, &find)) != NULL) {
701 		error = EBADF;
702 		goto reply;
703 	}
704 	cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
705 	if (cf->closed) {
706 		error = EBADF;
707 		goto reply;
708 	}
709 
710 	cf->fd = -1;
711 	if (msg->fd == -1)
712 		cf->fd = open(path, flags);
713 	else if (allow_streams) {
714 		if (msg->fd != STDIN_FILENO)
715 			errno = EBADF;
716 		else {
717 			cf->fd = dup(msg->fd);
718 			if (close_received)
719 				close(msg->fd); /* can only be used once */
720 		}
721 	} else
722 		errno = EBADF;
723 	if (cf->fd == -1) {
724 		error = errno;
725 		goto reply;
726 	}
727 
728 	cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
729 	    file_read_error_callback, cf);
730 	bufferevent_enable(cf->event, EV_READ);
731 	return;
732 
733 reply:
734 	reply.stream = msg->stream;
735 	reply.error = error;
736 	proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
737 }
738 
739 /* Handle a write ready message (server). */
740 void
741 file_write_ready(struct client_files *files, struct imsg *imsg)
742 {
743 	struct msg_write_ready	*msg = imsg->data;
744 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
745 	struct client_file	 find, *cf;
746 
747 	if (msglen != sizeof *msg)
748 		fatalx("bad MSG_WRITE_READY size");
749 	find.stream = msg->stream;
750 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
751 		return;
752 	if (msg->error != 0) {
753 		cf->error = msg->error;
754 		file_fire_done(cf);
755 	} else
756 		file_push(cf);
757 }
758 
759 /* Handle read data message (server). */
760 void
761 file_read_data(struct client_files *files, struct imsg *imsg)
762 {
763 	struct msg_read_data	*msg = imsg->data;
764 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
765 	struct client_file	 find, *cf;
766 	void			*bdata = msg + 1;
767 	size_t			 bsize = msglen - sizeof *msg;
768 
769 	if (msglen < sizeof *msg)
770 		fatalx("bad MSG_READ_DATA size");
771 	find.stream = msg->stream;
772 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
773 		return;
774 
775 	log_debug("file %d read %zu bytes", cf->stream, bsize);
776 	if (cf->error == 0) {
777 		if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
778 			cf->error = ENOMEM;
779 			file_fire_done(cf);
780 		} else
781 			file_fire_read(cf);
782 	}
783 }
784 
785 /* Handle a read done message (server). */
786 void
787 file_read_done(struct client_files *files, struct imsg *imsg)
788 {
789 	struct msg_read_done	*msg = imsg->data;
790 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
791 	struct client_file	 find, *cf;
792 
793 	if (msglen != sizeof *msg)
794 		fatalx("bad MSG_READ_DONE size");
795 	find.stream = msg->stream;
796 	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
797 		return;
798 
799 	log_debug("file %d read done", cf->stream);
800 	cf->error = msg->error;
801 	file_fire_done(cf);
802 }
803