1/* $OpenBSD: file.c,v 1.15 2023/04/17 17:58:35 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
39static int	file_next_stream = 3;
40
41RB_GENERATE(client_files, client_file, entry, file_cmp);
42
43/* Get path for file, either as given or from working directory. */
44static char *
45file_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. */
57int
58file_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 */
73struct client_file *
74file_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. */
99struct client_file *
100file_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. */
131void
132file_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. */
149static void
150file_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 &&
156	    (cf->closed || c == NULL || (~c->flags & CLIENT_DEAD)))
157		cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
158	file_free(cf);
159}
160
161/* Add an event to fire the done callback (used by the server). */
162void
163file_fire_done(struct client_file *cf)
164{
165	event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
166}
167
168/* Fire the read callback. */
169void
170file_fire_read(struct client_file *cf)
171{
172	if (cf->cb != NULL)
173		cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data);
174}
175
176/* Can this file be printed to? */
177int
178file_can_print(struct client *c)
179{
180	if (c == NULL ||
181	    (c->flags & CLIENT_ATTACHED) ||
182	    (c->flags & CLIENT_CONTROL))
183		return (0);
184	return (1);
185}
186
187/* Print a message to a file. */
188void
189file_print(struct client *c, const char *fmt, ...)
190{
191	va_list	ap;
192
193	va_start(ap, fmt);
194	file_vprint(c, fmt, ap);
195	va_end(ap);
196}
197
198/* Print a message to a file. */
199void
200file_vprint(struct client *c, const char *fmt, va_list ap)
201{
202	struct client_file	 find, *cf;
203	struct msg_write_open	 msg;
204
205	if (!file_can_print(c))
206		return;
207
208	find.stream = 1;
209	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
210		cf = file_create_with_client(c, 1, NULL, NULL);
211		cf->path = xstrdup("-");
212
213		evbuffer_add_vprintf(cf->buffer, fmt, ap);
214
215		msg.stream = 1;
216		msg.fd = STDOUT_FILENO;
217		msg.flags = 0;
218		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
219	} else {
220		evbuffer_add_vprintf(cf->buffer, fmt, ap);
221		file_push(cf);
222	}
223}
224
225/* Print a buffer to a file. */
226void
227file_print_buffer(struct client *c, void *data, size_t size)
228{
229	struct client_file	 find, *cf;
230	struct msg_write_open	 msg;
231
232	if (!file_can_print(c))
233		return;
234
235	find.stream = 1;
236	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
237		cf = file_create_with_client(c, 1, NULL, NULL);
238		cf->path = xstrdup("-");
239
240		evbuffer_add(cf->buffer, data, size);
241
242		msg.stream = 1;
243		msg.fd = STDOUT_FILENO;
244		msg.flags = 0;
245		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
246	} else {
247		evbuffer_add(cf->buffer, data, size);
248		file_push(cf);
249	}
250}
251
252/* Report an error to a file. */
253void
254file_error(struct client *c, const char *fmt, ...)
255{
256	struct client_file	 find, *cf;
257	struct msg_write_open	 msg;
258	va_list			 ap;
259
260	if (!file_can_print(c))
261		return;
262
263	va_start(ap, fmt);
264
265	find.stream = 2;
266	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
267		cf = file_create_with_client(c, 2, NULL, NULL);
268		cf->path = xstrdup("-");
269
270		evbuffer_add_vprintf(cf->buffer, fmt, ap);
271
272		msg.stream = 2;
273		msg.fd = STDERR_FILENO;
274		msg.flags = 0;
275		proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
276	} else {
277		evbuffer_add_vprintf(cf->buffer, fmt, ap);
278		file_push(cf);
279	}
280
281	va_end(ap);
282}
283
284/* Write data to a file. */
285void
286file_write(struct client *c, const char *path, int flags, const void *bdata,
287    size_t bsize, client_file_cb cb, void *cbdata)
288{
289	struct client_file	*cf;
290	struct msg_write_open	*msg;
291	size_t			 msglen;
292	int			 fd = -1;
293	u_int			 stream = file_next_stream++;
294	FILE			*f;
295	const char		*mode;
296
297	if (strcmp(path, "-") == 0) {
298		cf = file_create_with_client(c, stream, cb, cbdata);
299		cf->path = xstrdup("-");
300
301		fd = STDOUT_FILENO;
302		if (c == NULL ||
303		    (c->flags & CLIENT_ATTACHED) ||
304		    (c->flags & CLIENT_CONTROL)) {
305			cf->error = EBADF;
306			goto done;
307		}
308		goto skip;
309	}
310
311	cf = file_create_with_client(c, stream, cb, cbdata);
312	cf->path = file_get_path(c, path);
313
314	if (c == NULL || c->flags & CLIENT_ATTACHED) {
315		if (flags & O_APPEND)
316			mode = "ab";
317		else
318			mode = "wb";
319		f = fopen(cf->path, mode);
320		if (f == NULL) {
321			cf->error = errno;
322			goto done;
323		}
324		if (fwrite(bdata, 1, bsize, f) != bsize) {
325			fclose(f);
326			cf->error = EIO;
327			goto done;
328		}
329		fclose(f);
330		goto done;
331	}
332
333skip:
334	evbuffer_add(cf->buffer, bdata, bsize);
335
336	msglen = strlen(cf->path) + 1 + sizeof *msg;
337	if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
338		cf->error = E2BIG;
339		goto done;
340	}
341	msg = xmalloc(msglen);
342	msg->stream = cf->stream;
343	msg->fd = fd;
344	msg->flags = flags;
345	memcpy(msg + 1, cf->path, msglen - sizeof *msg);
346	if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
347		free(msg);
348		cf->error = EINVAL;
349		goto done;
350	}
351	free(msg);
352	return;
353
354done:
355	file_fire_done(cf);
356}
357
358/* Read a file. */
359struct client_file *
360file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
361{
362	struct client_file	*cf;
363	struct msg_read_open	*msg;
364	size_t			 msglen;
365	int			 fd = -1;
366	u_int			 stream = file_next_stream++;
367	FILE			*f;
368	size_t			 size;
369	char			 buffer[BUFSIZ];
370
371	if (strcmp(path, "-") == 0) {
372		cf = file_create_with_client(c, stream, cb, cbdata);
373		cf->path = xstrdup("-");
374
375		fd = STDIN_FILENO;
376		if (c == NULL ||
377		    (c->flags & CLIENT_ATTACHED) ||
378		    (c->flags & CLIENT_CONTROL)) {
379			cf->error = EBADF;
380			goto done;
381		}
382		goto skip;
383	}
384
385	cf = file_create_with_client(c, stream, cb, cbdata);
386	cf->path = file_get_path(c, path);
387
388	if (c == NULL || c->flags & CLIENT_ATTACHED) {
389		f = fopen(cf->path, "rb");
390		if (f == NULL) {
391			cf->error = errno;
392			goto done;
393		}
394		for (;;) {
395			size = fread(buffer, 1, sizeof buffer, f);
396			if (evbuffer_add(cf->buffer, buffer, size) != 0) {
397				cf->error = ENOMEM;
398				goto done;
399			}
400			if (size != sizeof buffer)
401				break;
402		}
403		if (ferror(f)) {
404			cf->error = EIO;
405			goto done;
406		}
407		fclose(f);
408		goto done;
409	}
410
411skip:
412	msglen = strlen(cf->path) + 1 + sizeof *msg;
413	if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
414		cf->error = E2BIG;
415		goto done;
416	}
417	msg = xmalloc(msglen);
418	msg->stream = cf->stream;
419	msg->fd = fd;
420	memcpy(msg + 1, cf->path, msglen - sizeof *msg);
421	if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
422		free(msg);
423		cf->error = EINVAL;
424		goto done;
425	}
426	free(msg);
427	return cf;
428
429done:
430	file_fire_done(cf);
431	return NULL;
432}
433
434/* Cancel a file read. */
435void
436file_cancel(struct client_file *cf)
437{
438	struct msg_read_cancel	 msg;
439
440	log_debug("read cancel file %d", cf->stream);
441
442	if (cf->closed)
443		return;
444	cf->closed = 1;
445
446	msg.stream = cf->stream;
447	proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
448}
449
450/* Push event, fired if there is more writing to be done. */
451static void
452file_push_cb(__unused int fd, __unused short events, void *arg)
453{
454	struct client_file	*cf = arg;
455
456	if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
457		file_push(cf);
458	file_free(cf);
459}
460
461/* Push uwritten data to the client for a file, if it will accept it. */
462void
463file_push(struct client_file *cf)
464{
465	struct msg_write_data	*msg;
466	size_t			 msglen, sent, left;
467	struct msg_write_close	 close;
468
469	msg = xmalloc(sizeof *msg);
470	left = EVBUFFER_LENGTH(cf->buffer);
471	while (left != 0) {
472		sent = left;
473		if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
474			sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
475
476		msglen = (sizeof *msg) + sent;
477		msg = xrealloc(msg, msglen);
478		msg->stream = cf->stream;
479		memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
480		if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
481			break;
482		evbuffer_drain(cf->buffer, sent);
483
484		left = EVBUFFER_LENGTH(cf->buffer);
485		log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
486	}
487	if (left != 0) {
488		cf->references++;
489		event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
490	} else if (cf->stream > 2) {
491		close.stream = cf->stream;
492		proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
493		file_fire_done(cf);
494	}
495	free(msg);
496}
497
498/* Check if any files have data left to write. */
499int
500file_write_left(struct client_files *files)
501{
502	struct client_file	*cf;
503	size_t			 left;
504	int			 waiting = 0;
505
506	RB_FOREACH(cf, client_files, files) {
507		if (cf->event == NULL)
508			continue;
509		left = EVBUFFER_LENGTH(cf->event->output);
510		if (left != 0) {
511			waiting++;
512			log_debug("file %u %zu bytes left", cf->stream, left);
513		}
514	}
515	return (waiting != 0);
516}
517
518/* Client file write error callback. */
519static void
520file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
521    void *arg)
522{
523	struct client_file	*cf = arg;
524
525	log_debug("write error file %d", cf->stream);
526
527	bufferevent_free(cf->event);
528	cf->event = NULL;
529
530	close(cf->fd);
531	cf->fd = -1;
532
533	if (cf->cb != NULL)
534		cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
535}
536
537/* Client file write callback. */
538static void
539file_write_callback(__unused struct bufferevent *bev, void *arg)
540{
541	struct client_file	*cf = arg;
542
543	log_debug("write check file %d", cf->stream);
544
545	if (cf->cb != NULL)
546		cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
547
548	if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
549		bufferevent_free(cf->event);
550		close(cf->fd);
551		RB_REMOVE(client_files, cf->tree, cf);
552		file_free(cf);
553	}
554}
555
556/* Handle a file write open message (client). */
557void
558file_write_open(struct client_files *files, struct tmuxpeer *peer,
559    struct imsg *imsg, int allow_streams, int close_received,
560    client_file_cb cb, void *cbdata)
561{
562	struct msg_write_open	*msg = imsg->data;
563	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
564	const char		*path;
565	struct msg_write_ready	 reply;
566	struct client_file	 find, *cf;
567	const int		 flags = O_NONBLOCK|O_WRONLY|O_CREAT;
568	int			 error = 0;
569
570	if (msglen < sizeof *msg)
571		fatalx("bad MSG_WRITE_OPEN size");
572	if (msglen == sizeof *msg)
573		path = "-";
574	else
575		path = (const char *)(msg + 1);
576	log_debug("open write file %d %s", msg->stream, path);
577
578	find.stream = msg->stream;
579	if (RB_FIND(client_files, files, &find) != NULL) {
580		error = EBADF;
581		goto reply;
582	}
583	cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
584	if (cf->closed) {
585		error = EBADF;
586		goto reply;
587	}
588
589	cf->fd = -1;
590	if (msg->fd == -1)
591		cf->fd = open(path, msg->flags|flags, 0644);
592	else if (allow_streams) {
593		if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
594			errno = EBADF;
595		else {
596			cf->fd = dup(msg->fd);
597			if (close_received)
598				close(msg->fd); /* can only be used once */
599		}
600	} else
601	      errno = EBADF;
602	if (cf->fd == -1) {
603		error = errno;
604		goto reply;
605	}
606
607	cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
608	    file_write_error_callback, cf);
609	if (cf->event == NULL)
610		fatalx("out of memory");
611	bufferevent_enable(cf->event, EV_WRITE);
612	goto reply;
613
614reply:
615	reply.stream = msg->stream;
616	reply.error = error;
617	proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
618}
619
620/* Handle a file write data message (client). */
621void
622file_write_data(struct client_files *files, struct imsg *imsg)
623{
624	struct msg_write_data	*msg = imsg->data;
625	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
626	struct client_file	 find, *cf;
627	size_t			 size = msglen - sizeof *msg;
628
629	if (msglen < sizeof *msg)
630		fatalx("bad MSG_WRITE size");
631	find.stream = msg->stream;
632	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
633		fatalx("unknown stream number");
634	log_debug("write %zu to file %d", size, cf->stream);
635
636	if (cf->event != NULL)
637		bufferevent_write(cf->event, msg + 1, size);
638}
639
640/* Handle a file write close message (client). */
641void
642file_write_close(struct client_files *files, struct imsg *imsg)
643{
644	struct msg_write_close	*msg = imsg->data;
645	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
646	struct client_file	 find, *cf;
647
648	if (msglen != sizeof *msg)
649		fatalx("bad MSG_WRITE_CLOSE size");
650	find.stream = msg->stream;
651	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
652		fatalx("unknown stream number");
653	log_debug("close file %d", cf->stream);
654
655	if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
656		if (cf->event != NULL)
657			bufferevent_free(cf->event);
658		if (cf->fd != -1)
659			close(cf->fd);
660		RB_REMOVE(client_files, files, cf);
661		file_free(cf);
662	}
663}
664
665/* Client file read error callback. */
666static void
667file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
668    void *arg)
669{
670	struct client_file	*cf = arg;
671	struct msg_read_done	 msg;
672
673	log_debug("read error file %d", cf->stream);
674
675	msg.stream = cf->stream;
676	msg.error = 0;
677	proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
678
679	bufferevent_free(cf->event);
680	close(cf->fd);
681	RB_REMOVE(client_files, cf->tree, cf);
682	file_free(cf);
683}
684
685/* Client file read callback. */
686static void
687file_read_callback(__unused struct bufferevent *bev, void *arg)
688{
689	struct client_file	*cf = arg;
690	void			*bdata;
691	size_t			 bsize;
692	struct msg_read_data	*msg;
693	size_t			 msglen;
694
695	msg = xmalloc(sizeof *msg);
696	for (;;) {
697		bdata = EVBUFFER_DATA(cf->event->input);
698		bsize = EVBUFFER_LENGTH(cf->event->input);
699
700		if (bsize == 0)
701			break;
702		if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
703			bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
704		log_debug("read %zu from file %d", bsize, cf->stream);
705
706		msglen = (sizeof *msg) + bsize;
707		msg = xrealloc(msg, msglen);
708		msg->stream = cf->stream;
709		memcpy(msg + 1, bdata, bsize);
710		proc_send(cf->peer, MSG_READ, -1, msg, msglen);
711
712		evbuffer_drain(cf->event->input, bsize);
713	}
714	free(msg);
715}
716
717/* Handle a file read open message (client). */
718void
719file_read_open(struct client_files *files, struct tmuxpeer *peer,
720    struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb,
721    void *cbdata)
722{
723	struct msg_read_open	*msg = imsg->data;
724	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
725	const char		*path;
726	struct msg_read_done	 reply;
727	struct client_file	 find, *cf;
728	const int		 flags = O_NONBLOCK|O_RDONLY;
729	int			 error;
730
731	if (msglen < sizeof *msg)
732		fatalx("bad MSG_READ_OPEN size");
733	if (msglen == sizeof *msg)
734		path = "-";
735	else
736		path = (const char *)(msg + 1);
737	log_debug("open read file %d %s", msg->stream, path);
738
739	find.stream = msg->stream;
740	if (RB_FIND(client_files, files, &find) != NULL) {
741		error = EBADF;
742		goto reply;
743	}
744	cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
745	if (cf->closed) {
746		error = EBADF;
747		goto reply;
748	}
749
750	cf->fd = -1;
751	if (msg->fd == -1)
752		cf->fd = open(path, flags);
753	else if (allow_streams) {
754		if (msg->fd != STDIN_FILENO)
755			errno = EBADF;
756		else {
757			cf->fd = dup(msg->fd);
758			if (close_received)
759				close(msg->fd); /* can only be used once */
760		}
761	} else
762		errno = EBADF;
763	if (cf->fd == -1) {
764		error = errno;
765		goto reply;
766	}
767
768	cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
769	    file_read_error_callback, cf);
770	if (cf->event == NULL)
771		fatalx("out of memory");
772	bufferevent_enable(cf->event, EV_READ);
773	return;
774
775reply:
776	reply.stream = msg->stream;
777	reply.error = error;
778	proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
779}
780
781/* Handle a read cancel message (client). */
782void
783file_read_cancel(struct client_files *files, struct imsg *imsg)
784{
785	struct msg_read_cancel	*msg = imsg->data;
786	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
787	struct client_file	 find, *cf;
788
789	if (msglen != sizeof *msg)
790		fatalx("bad MSG_READ_CANCEL size");
791	find.stream = msg->stream;
792	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
793		fatalx("unknown stream number");
794	log_debug("cancel file %d", cf->stream);
795
796	file_read_error_callback(NULL, 0, cf);
797}
798
799/* Handle a write ready message (server). */
800void
801file_write_ready(struct client_files *files, struct imsg *imsg)
802{
803	struct msg_write_ready	*msg = imsg->data;
804	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
805	struct client_file	 find, *cf;
806
807	if (msglen != sizeof *msg)
808		fatalx("bad MSG_WRITE_READY size");
809	find.stream = msg->stream;
810	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
811		return;
812	if (msg->error != 0) {
813		cf->error = msg->error;
814		file_fire_done(cf);
815	} else
816		file_push(cf);
817}
818
819/* Handle read data message (server). */
820void
821file_read_data(struct client_files *files, struct imsg *imsg)
822{
823	struct msg_read_data	*msg = imsg->data;
824	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
825	struct client_file	 find, *cf;
826	void			*bdata = msg + 1;
827	size_t			 bsize = msglen - sizeof *msg;
828
829	if (msglen < sizeof *msg)
830		fatalx("bad MSG_READ_DATA size");
831	find.stream = msg->stream;
832	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
833		return;
834
835	log_debug("file %d read %zu bytes", cf->stream, bsize);
836	if (cf->error == 0 && !cf->closed) {
837		if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
838			cf->error = ENOMEM;
839			file_fire_done(cf);
840		} else
841			file_fire_read(cf);
842	}
843}
844
845/* Handle a read done message (server). */
846void
847file_read_done(struct client_files *files, struct imsg *imsg)
848{
849	struct msg_read_done	*msg = imsg->data;
850	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
851	struct client_file	 find, *cf;
852
853	if (msglen != sizeof *msg)
854		fatalx("bad MSG_READ_DONE size");
855	find.stream = msg->stream;
856	if ((cf = RB_FIND(client_files, files, &find)) == NULL)
857		return;
858
859	log_debug("file %d read done", cf->stream);
860	cf->error = msg->error;
861	file_fire_done(cf);
862}
863