xref: /openbsd-src/usr.bin/rsync/io.c (revision d9a51c353c88dac7b4a389c112b4cfe97b8e3a46)
1*d9a51c35Sjmc /*	$OpenBSD: io.c,v 1.23 2022/12/26 19:16:02 jmc Exp $ */
260a32ee9Sbenno /*
360a32ee9Sbenno  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
460a32ee9Sbenno  *
560a32ee9Sbenno  * Permission to use, copy, modify, and distribute this software for any
660a32ee9Sbenno  * purpose with or without fee is hereby granted, provided that the above
760a32ee9Sbenno  * copyright notice and this permission notice appear in all copies.
860a32ee9Sbenno  *
960a32ee9Sbenno  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1060a32ee9Sbenno  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1160a32ee9Sbenno  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1260a32ee9Sbenno  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1360a32ee9Sbenno  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1460a32ee9Sbenno  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1560a32ee9Sbenno  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1660a32ee9Sbenno  */
1760a32ee9Sbenno #include <sys/stat.h>
1860a32ee9Sbenno 
1960a32ee9Sbenno #include <assert.h>
2060a32ee9Sbenno #include <endian.h>
2160a32ee9Sbenno #include <errno.h>
2260a32ee9Sbenno #include <poll.h>
2360a32ee9Sbenno #include <stdint.h>
2460a32ee9Sbenno #include <stdio.h>
2560a32ee9Sbenno #include <stdlib.h>
2660a32ee9Sbenno #include <string.h>
2760a32ee9Sbenno #include <unistd.h>
2860a32ee9Sbenno 
2960a32ee9Sbenno #include "extern.h"
3060a32ee9Sbenno 
31cf0de74fSflorian /*
32cf0de74fSflorian  * A non-blocking check to see whether there's POLLIN data in fd.
33cf0de74fSflorian  * Returns <0 on failure, 0 if there's no data, >0 if there is.
34cf0de74fSflorian  */
3560a32ee9Sbenno int
io_read_check(int fd)36ba617adaSbenno io_read_check(int fd)
3760a32ee9Sbenno {
3860a32ee9Sbenno 	struct pollfd	pfd;
3960a32ee9Sbenno 
4060a32ee9Sbenno 	pfd.fd = fd;
4160a32ee9Sbenno 	pfd.events = POLLIN;
4260a32ee9Sbenno 
433aaa63ebSderaadt 	if (poll(&pfd, 1, 0) == -1) {
44b2a7eac7Sbenno 		ERR("poll");
4560a32ee9Sbenno 		return -1;
4660a32ee9Sbenno 	}
47cf0de74fSflorian 	return (pfd.revents & POLLIN);
4860a32ee9Sbenno }
4960a32ee9Sbenno 
5060a32ee9Sbenno /*
5160a32ee9Sbenno  * Write buffer to non-blocking descriptor.
5260a32ee9Sbenno  * Returns zero on failure, non-zero on success (zero or more bytes).
53cf0de74fSflorian  * On success, fills in "sz" with the amount written.
5460a32ee9Sbenno  */
5560a32ee9Sbenno static int
io_write_nonblocking(int fd,const void * buf,size_t bsz,size_t * sz)56ba617adaSbenno io_write_nonblocking(int fd, const void *buf, size_t bsz,
57b1d34d51Sderaadt     size_t *sz)
5860a32ee9Sbenno {
5960a32ee9Sbenno 	struct pollfd	pfd;
6060a32ee9Sbenno 	ssize_t		wsz;
610889042fSflorian 	int		c;
6260a32ee9Sbenno 
6360a32ee9Sbenno 	*sz = 0;
6460a32ee9Sbenno 
65f1dcb30aSderaadt 	if (bsz == 0)
6660a32ee9Sbenno 		return 1;
6760a32ee9Sbenno 
6860a32ee9Sbenno 	pfd.fd = fd;
6960a32ee9Sbenno 	pfd.events = POLLOUT;
7060a32ee9Sbenno 
71cf0de74fSflorian 	/* Poll and check for all possible errors. */
72cf0de74fSflorian 
73a84b4914Sclaudio 	if ((c = poll(&pfd, 1, poll_timeout)) == -1) {
74b2a7eac7Sbenno 		ERR("poll");
7560a32ee9Sbenno 		return 0;
760889042fSflorian 	} else if (c == 0) {
77b2a7eac7Sbenno 		ERRX("poll: timeout");
780889042fSflorian 		return 0;
79cf0de74fSflorian 	} else if ((pfd.revents & (POLLERR|POLLNVAL))) {
80b2a7eac7Sbenno 		ERRX("poll: bad fd");
8160a32ee9Sbenno 		return 0;
8260a32ee9Sbenno 	} else if ((pfd.revents & POLLHUP)) {
83b2a7eac7Sbenno 		ERRX("poll: hangup");
8460a32ee9Sbenno 		return 0;
8560a32ee9Sbenno 	} else if (!(pfd.revents & POLLOUT)) {
86b2a7eac7Sbenno 		ERRX("poll: unknown event");
8760a32ee9Sbenno 		return 0;
8860a32ee9Sbenno 	}
8960a32ee9Sbenno 
90cf0de74fSflorian 	/* Now the non-blocking write. */
91cf0de74fSflorian 
923aaa63ebSderaadt 	if ((wsz = write(fd, buf, bsz)) == -1) {
93b2a7eac7Sbenno 		ERR("write");
9460a32ee9Sbenno 		return 0;
9560a32ee9Sbenno 	}
9660a32ee9Sbenno 
9760a32ee9Sbenno 	*sz = wsz;
9860a32ee9Sbenno 	return 1;
9960a32ee9Sbenno }
10060a32ee9Sbenno 
10160a32ee9Sbenno /*
10260a32ee9Sbenno  * Blocking write of the full size of the buffer.
10360a32ee9Sbenno  * Returns 0 on failure, non-zero on success (all bytes written).
10460a32ee9Sbenno  */
10560a32ee9Sbenno static int
io_write_blocking(int fd,const void * buf,size_t sz)106ba617adaSbenno io_write_blocking(int fd, const void *buf, size_t sz)
10760a32ee9Sbenno {
10860a32ee9Sbenno 	size_t		wsz;
10960a32ee9Sbenno 	int		c;
11060a32ee9Sbenno 
11160a32ee9Sbenno 	while (sz > 0) {
112ba617adaSbenno 		c = io_write_nonblocking(fd, buf, sz, &wsz);
11360a32ee9Sbenno 		if (!c) {
114b2a7eac7Sbenno 			ERRX1("io_write_nonblocking");
11560a32ee9Sbenno 			return 0;
116f1dcb30aSderaadt 		} else if (wsz == 0) {
117b2a7eac7Sbenno 			ERRX("io_write_nonblocking: short write");
11860a32ee9Sbenno 			return 0;
11960a32ee9Sbenno 		}
12060a32ee9Sbenno 		buf += wsz;
12160a32ee9Sbenno 		sz -= wsz;
12260a32ee9Sbenno 	}
12360a32ee9Sbenno 
12460a32ee9Sbenno 	return 1;
12560a32ee9Sbenno }
12660a32ee9Sbenno 
12760a32ee9Sbenno /*
12860a32ee9Sbenno  * Write "buf" of size "sz" to non-blocking descriptor.
12960a32ee9Sbenno  * Returns zero on failure, non-zero on success (all bytes written to
13060a32ee9Sbenno  * the descriptor).
13160a32ee9Sbenno  */
13260a32ee9Sbenno int
io_write_buf(struct sess * sess,int fd,const void * buf,size_t sz)13360a32ee9Sbenno io_write_buf(struct sess *sess, int fd, const void *buf, size_t sz)
13460a32ee9Sbenno {
13560a32ee9Sbenno 	int32_t	 tag, tagbuf;
13660a32ee9Sbenno 	size_t	 wsz;
13760a32ee9Sbenno 	int	 c;
13860a32ee9Sbenno 
13960a32ee9Sbenno 	if (!sess->mplex_writes) {
140ba617adaSbenno 		c = io_write_blocking(fd, buf, sz);
14160a32ee9Sbenno 		sess->total_write += sz;
14260a32ee9Sbenno 		return c;
14360a32ee9Sbenno 	}
14460a32ee9Sbenno 
14560a32ee9Sbenno 	while (sz > 0) {
146c91bd412Sclaudio 		wsz = (sz < 0xFFFFFF) ? sz : 0xFFFFFF;
14760a32ee9Sbenno 		tag = (7 << 24) + wsz;
14860a32ee9Sbenno 		tagbuf = htole32(tag);
149ba617adaSbenno 		if (!io_write_blocking(fd, &tagbuf, sizeof(tagbuf))) {
150b2a7eac7Sbenno 			ERRX1("io_write_blocking");
15160a32ee9Sbenno 			return 0;
15260a32ee9Sbenno 		}
153ba617adaSbenno 		if (!io_write_blocking(fd, buf, wsz)) {
154b2a7eac7Sbenno 			ERRX1("io_write_blocking");
15560a32ee9Sbenno 			return 0;
15660a32ee9Sbenno 		}
15760a32ee9Sbenno 		sess->total_write += wsz;
15860a32ee9Sbenno 		sz -= wsz;
15960a32ee9Sbenno 		buf += wsz;
16060a32ee9Sbenno 	}
16160a32ee9Sbenno 
16260a32ee9Sbenno 	return 1;
16360a32ee9Sbenno }
16460a32ee9Sbenno 
16560a32ee9Sbenno /*
16660a32ee9Sbenno  * Write "line" (NUL-terminated) followed by a newline.
167*d9a51c35Sjmc  * Returns zero on failure, non-zero on success.
16860a32ee9Sbenno  */
16960a32ee9Sbenno int
io_write_line(struct sess * sess,int fd,const char * line)17060a32ee9Sbenno io_write_line(struct sess *sess, int fd, const char *line)
17160a32ee9Sbenno {
17260a32ee9Sbenno 
17360a32ee9Sbenno 	if (!io_write_buf(sess, fd, line, strlen(line)))
174b2a7eac7Sbenno 		ERRX1("io_write_buf");
17560a32ee9Sbenno 	else if (!io_write_byte(sess, fd, '\n'))
176b2a7eac7Sbenno 		ERRX1("io_write_byte");
17760a32ee9Sbenno 	else
17860a32ee9Sbenno 		return 1;
17960a32ee9Sbenno 
18060a32ee9Sbenno 	return 0;
18160a32ee9Sbenno }
18260a32ee9Sbenno 
18360a32ee9Sbenno /*
18460a32ee9Sbenno  * Read buffer from non-blocking descriptor.
18560a32ee9Sbenno  * Returns zero on failure, non-zero on success (zero or more bytes).
18660a32ee9Sbenno  */
18760a32ee9Sbenno static int
io_read_nonblocking(int fd,void * buf,size_t bsz,size_t * sz)188ba617adaSbenno io_read_nonblocking(int fd, void *buf, size_t bsz, size_t *sz)
18960a32ee9Sbenno {
19060a32ee9Sbenno 	struct pollfd	pfd;
19160a32ee9Sbenno 	ssize_t		rsz;
1920889042fSflorian 	int		c;
19360a32ee9Sbenno 
19460a32ee9Sbenno 	*sz = 0;
19560a32ee9Sbenno 
196f1dcb30aSderaadt 	if (bsz == 0)
19760a32ee9Sbenno 		return 1;
19860a32ee9Sbenno 
19960a32ee9Sbenno 	pfd.fd = fd;
20060a32ee9Sbenno 	pfd.events = POLLIN;
20160a32ee9Sbenno 
202cf0de74fSflorian 	/* Poll and check for all possible errors. */
203cf0de74fSflorian 
204a84b4914Sclaudio 	if ((c = poll(&pfd, 1, poll_timeout)) == -1) {
205b2a7eac7Sbenno 		ERR("poll");
20660a32ee9Sbenno 		return 0;
2070889042fSflorian 	} else if (c == 0) {
208b2a7eac7Sbenno 		ERRX("poll: timeout");
2090889042fSflorian 		return 0;
210cf0de74fSflorian 	} else if ((pfd.revents & (POLLERR|POLLNVAL))) {
211b2a7eac7Sbenno 		ERRX("poll: bad fd");
21260a32ee9Sbenno 		return 0;
21360a32ee9Sbenno 	} else if (!(pfd.revents & (POLLIN|POLLHUP))) {
214b2a7eac7Sbenno 		ERRX("poll: unknown event");
21560a32ee9Sbenno 		return 0;
21660a32ee9Sbenno 	}
21760a32ee9Sbenno 
218cf0de74fSflorian 	/* Now the non-blocking read, checking for EOF. */
219cf0de74fSflorian 
2203aaa63ebSderaadt 	if ((rsz = read(fd, buf, bsz)) == -1) {
221b2a7eac7Sbenno 		ERR("read");
22260a32ee9Sbenno 		return 0;
223f1dcb30aSderaadt 	} else if (rsz == 0) {
224b2a7eac7Sbenno 		ERRX("unexpected end of file");
22560a32ee9Sbenno 		return 0;
22660a32ee9Sbenno 	}
22760a32ee9Sbenno 
22860a32ee9Sbenno 	*sz = rsz;
22960a32ee9Sbenno 	return 1;
23060a32ee9Sbenno }
23160a32ee9Sbenno 
23260a32ee9Sbenno /*
23360a32ee9Sbenno  * Blocking read of the full size of the buffer.
23460a32ee9Sbenno  * This can be called from either the error type message or a regular
23560a32ee9Sbenno  * message---or for that matter, multiplexed or not.
23660a32ee9Sbenno  * Returns 0 on failure, non-zero on success (all bytes read).
23760a32ee9Sbenno  */
23860a32ee9Sbenno static int
io_read_blocking(int fd,void * buf,size_t sz)239ba617adaSbenno io_read_blocking(int fd, void *buf, size_t sz)
24060a32ee9Sbenno {
24160a32ee9Sbenno 	size_t	 rsz;
24260a32ee9Sbenno 	int	 c;
24360a32ee9Sbenno 
24460a32ee9Sbenno 	while (sz > 0) {
245ba617adaSbenno 		c = io_read_nonblocking(fd, buf, sz, &rsz);
24660a32ee9Sbenno 		if (!c) {
247b2a7eac7Sbenno 			ERRX1("io_read_nonblocking");
24860a32ee9Sbenno 			return 0;
249f1dcb30aSderaadt 		} else if (rsz == 0) {
250b2a7eac7Sbenno 			ERRX("io_read_nonblocking: short read");
25160a32ee9Sbenno 			return 0;
25260a32ee9Sbenno 		}
25360a32ee9Sbenno 		buf += rsz;
25460a32ee9Sbenno 		sz -= rsz;
25560a32ee9Sbenno 	}
25660a32ee9Sbenno 
25760a32ee9Sbenno 	return 1;
25860a32ee9Sbenno }
25960a32ee9Sbenno 
26060a32ee9Sbenno /*
26160a32ee9Sbenno  * When we do a lot of writes in a row (such as when the sender emits
26260a32ee9Sbenno  * the file list), the server might be sending us multiplexed log
26360a32ee9Sbenno  * messages.
26460a32ee9Sbenno  * If it sends too many, it clogs the socket.
26560a32ee9Sbenno  * This function looks into the read buffer and clears out any log
26660a32ee9Sbenno  * messages pending.
26760a32ee9Sbenno  * If called when there are valid data reads available, this function
26860a32ee9Sbenno  * does nothing.
26960a32ee9Sbenno  * Returns zero on failure, non-zero on success.
27060a32ee9Sbenno  */
27160a32ee9Sbenno int
io_read_flush(struct sess * sess,int fd)27260a32ee9Sbenno io_read_flush(struct sess *sess, int fd)
27360a32ee9Sbenno {
27460a32ee9Sbenno 	int32_t	 tagbuf, tag;
27560a32ee9Sbenno 	char	 mpbuf[1024];
27660a32ee9Sbenno 
27760a32ee9Sbenno 	if (sess->mplex_read_remain)
27860a32ee9Sbenno 		return 1;
27960a32ee9Sbenno 
28060a32ee9Sbenno 	/*
28160a32ee9Sbenno 	 * First, read the 4-byte multiplex tag.
28260a32ee9Sbenno 	 * The first byte is the tag identifier (7 for normal
28360a32ee9Sbenno 	 * data, !7 for out-of-band data), the last three are
28460a32ee9Sbenno 	 * for the remaining data size.
28560a32ee9Sbenno 	 */
28660a32ee9Sbenno 
287ba617adaSbenno 	if (!io_read_blocking(fd, &tagbuf, sizeof(tagbuf))) {
288b2a7eac7Sbenno 		ERRX1("io_read_blocking");
28960a32ee9Sbenno 		return 0;
29060a32ee9Sbenno 	}
29160a32ee9Sbenno 	tag = le32toh(tagbuf);
29260a32ee9Sbenno 	sess->mplex_read_remain = tag & 0xFFFFFF;
29360a32ee9Sbenno 	tag >>= 24;
294f1dcb30aSderaadt 	if (tag == 7)
29560a32ee9Sbenno 		return 1;
29660a32ee9Sbenno 
29760a32ee9Sbenno 	tag -= 7;
29860a32ee9Sbenno 
29960a32ee9Sbenno 	if (sess->mplex_read_remain > sizeof(mpbuf)) {
300b2a7eac7Sbenno 		ERRX("multiplex buffer overflow");
30160a32ee9Sbenno 		return 0;
302f1dcb30aSderaadt 	} else if (sess->mplex_read_remain == 0)
30360a32ee9Sbenno 		return 1;
30460a32ee9Sbenno 
305ba617adaSbenno 	if (!io_read_blocking(fd, mpbuf, sess->mplex_read_remain)) {
306b2a7eac7Sbenno 		ERRX1("io_read_blocking");
30760a32ee9Sbenno 		return 0;
30860a32ee9Sbenno 	}
309f1dcb30aSderaadt 	if (mpbuf[sess->mplex_read_remain - 1] == '\n')
31060a32ee9Sbenno 		mpbuf[--sess->mplex_read_remain] = '\0';
31160a32ee9Sbenno 
31260a32ee9Sbenno 	/*
31360a32ee9Sbenno 	 * Always print the server's messages, as the server
31460a32ee9Sbenno 	 * will control its own log levelling.
31560a32ee9Sbenno 	 */
31660a32ee9Sbenno 
317b2a7eac7Sbenno 	LOG0("%.*s", (int)sess->mplex_read_remain, mpbuf);
31860a32ee9Sbenno 	sess->mplex_read_remain = 0;
31960a32ee9Sbenno 
32060a32ee9Sbenno 	/*
32160a32ee9Sbenno 	 * I only know that a tag of one means an error.
32260a32ee9Sbenno 	 * This means that we should exit.
32360a32ee9Sbenno 	 */
32460a32ee9Sbenno 
325f1dcb30aSderaadt 	if (tag == 1) {
326b2a7eac7Sbenno 		ERRX1("error from remote host");
32760a32ee9Sbenno 		return 0;
32860a32ee9Sbenno 	}
32960a32ee9Sbenno 	return 1;
33060a32ee9Sbenno }
33160a32ee9Sbenno 
33260a32ee9Sbenno /*
33360a32ee9Sbenno  * Read buffer from non-blocking descriptor, possibly in multiplex read
33460a32ee9Sbenno  * mode.
33560a32ee9Sbenno  * Returns zero on failure, non-zero on success (all bytes read from
33660a32ee9Sbenno  * the descriptor).
33760a32ee9Sbenno  */
33860a32ee9Sbenno int
io_read_buf(struct sess * sess,int fd,void * buf,size_t sz)33960a32ee9Sbenno io_read_buf(struct sess *sess, int fd, void *buf, size_t sz)
34060a32ee9Sbenno {
34160a32ee9Sbenno 	size_t	 rsz;
34260a32ee9Sbenno 	int	 c;
34360a32ee9Sbenno 
34460a32ee9Sbenno 	/* If we're not multiplexing, read directly. */
34560a32ee9Sbenno 
34660a32ee9Sbenno 	if (!sess->mplex_reads) {
347f1dcb30aSderaadt 		assert(sess->mplex_read_remain == 0);
348ba617adaSbenno 		c = io_read_blocking(fd, buf, sz);
34960a32ee9Sbenno 		sess->total_read += sz;
35060a32ee9Sbenno 		return c;
35160a32ee9Sbenno 	}
35260a32ee9Sbenno 
35360a32ee9Sbenno 	while (sz > 0) {
35460a32ee9Sbenno 		/*
35560a32ee9Sbenno 		 * First, check to see if we have any regular data
35660a32ee9Sbenno 		 * hanging around waiting to be read.
35760a32ee9Sbenno 		 * If so, read the lesser of that data and whatever
35860a32ee9Sbenno 		 * amount we currently want.
35960a32ee9Sbenno 		 */
36060a32ee9Sbenno 
36160a32ee9Sbenno 		if (sess->mplex_read_remain) {
36260a32ee9Sbenno 			rsz = sess->mplex_read_remain < sz ?
36360a32ee9Sbenno 				sess->mplex_read_remain : sz;
364ba617adaSbenno 			if (!io_read_blocking(fd, buf, rsz)) {
365b2a7eac7Sbenno 				ERRX1("io_read_blocking");
36660a32ee9Sbenno 				return 0;
36760a32ee9Sbenno 			}
36860a32ee9Sbenno 			sz -= rsz;
36960a32ee9Sbenno 			sess->mplex_read_remain -= rsz;
37060a32ee9Sbenno 			buf += rsz;
37160a32ee9Sbenno 			sess->total_read += rsz;
37260a32ee9Sbenno 			continue;
37360a32ee9Sbenno 		}
37460a32ee9Sbenno 
375f1dcb30aSderaadt 		assert(sess->mplex_read_remain == 0);
37660a32ee9Sbenno 		if (!io_read_flush(sess, fd)) {
377b2a7eac7Sbenno 			ERRX1("io_read_flush");
37860a32ee9Sbenno 			return 0;
37960a32ee9Sbenno 		}
38060a32ee9Sbenno 	}
38160a32ee9Sbenno 
38260a32ee9Sbenno 	return 1;
38360a32ee9Sbenno }
38460a32ee9Sbenno 
385cf0de74fSflorian /*
386cf0de74fSflorian  * Like io_write_buf(), but for a long (which is a composite type).
387cf0de74fSflorian  * Returns zero on failure, non-zero on success.
388cf0de74fSflorian  */
38960a32ee9Sbenno int
io_write_ulong(struct sess * sess,int fd,uint64_t val)390aa1dcd86Sderaadt io_write_ulong(struct sess *sess, int fd, uint64_t val)
39160a32ee9Sbenno {
392aa1dcd86Sderaadt 	uint64_t	nv;
393aa1dcd86Sderaadt 	int64_t		sval = (int64_t)val;
39460a32ee9Sbenno 
39560a32ee9Sbenno 	/* Short-circuit: send as an integer if possible. */
39660a32ee9Sbenno 
397aa1dcd86Sderaadt 	if (sval <= INT32_MAX && sval >= 0) {
398cf0de74fSflorian 		if (!io_write_int(sess, fd, (int32_t)val)) {
399b2a7eac7Sbenno 			ERRX1("io_write_int");
400cf0de74fSflorian 			return 0;
401cf0de74fSflorian 		}
402cf0de74fSflorian 		return 1;
403cf0de74fSflorian 	}
40460a32ee9Sbenno 
405aa1dcd86Sderaadt 	/* Otherwise, pad with -1 32-bit, then send 64-bit. */
40660a32ee9Sbenno 
40760a32ee9Sbenno 	nv = htole64(val);
40860a32ee9Sbenno 
4094fbea989Sderaadt 	if (!io_write_int(sess, fd, -1))
410b2a7eac7Sbenno 		ERRX1("io_write_int");
41160a32ee9Sbenno 	else if (!io_write_buf(sess, fd, &nv, sizeof(int64_t)))
412b2a7eac7Sbenno 		ERRX1("io_write_buf");
41360a32ee9Sbenno 	else
41460a32ee9Sbenno 		return 1;
41560a32ee9Sbenno 
41660a32ee9Sbenno 	return 0;
41760a32ee9Sbenno }
41860a32ee9Sbenno 
419aa1dcd86Sderaadt int
io_write_long(struct sess * sess,int fd,int64_t val)420aa1dcd86Sderaadt io_write_long(struct sess *sess, int fd, int64_t val)
421aa1dcd86Sderaadt {
422aa1dcd86Sderaadt 	return io_write_ulong(sess, fd, (uint64_t)val);
423aa1dcd86Sderaadt }
424aa1dcd86Sderaadt 
425aa1dcd86Sderaadt /*
426aa1dcd86Sderaadt  * Like io_write_buf(), but for an unsigned integer.
427aa1dcd86Sderaadt  * Returns zero on failure, non-zero on success.
428aa1dcd86Sderaadt  */
429aa1dcd86Sderaadt int
io_write_uint(struct sess * sess,int fd,uint32_t val)430aa1dcd86Sderaadt io_write_uint(struct sess *sess, int fd, uint32_t val)
431aa1dcd86Sderaadt {
432aa1dcd86Sderaadt 	uint32_t	nv;
433aa1dcd86Sderaadt 
434aa1dcd86Sderaadt 	nv = htole32(val);
435aa1dcd86Sderaadt 
436aa1dcd86Sderaadt 	if (!io_write_buf(sess, fd, &nv, sizeof(uint32_t))) {
437b2a7eac7Sbenno 		ERRX1("io_write_buf");
438aa1dcd86Sderaadt 		return 0;
439aa1dcd86Sderaadt 	}
440aa1dcd86Sderaadt 	return 1;
441aa1dcd86Sderaadt }
442aa1dcd86Sderaadt 
443cf0de74fSflorian /*
444cf0de74fSflorian  * Like io_write_buf(), but for an integer.
445cf0de74fSflorian  * Returns zero on failure, non-zero on success.
446cf0de74fSflorian  */
44760a32ee9Sbenno int
io_write_int(struct sess * sess,int fd,int32_t val)44860a32ee9Sbenno io_write_int(struct sess *sess, int fd, int32_t val)
44960a32ee9Sbenno {
450aa1dcd86Sderaadt 	return io_write_uint(sess, fd, (uint32_t)val);
45160a32ee9Sbenno }
45260a32ee9Sbenno 
45360a32ee9Sbenno /*
45460a32ee9Sbenno  * A simple assertion-protected memory copy from th einput "val" or size
45560a32ee9Sbenno  * "valsz" into our buffer "buf", full size "buflen", position "bufpos".
45660a32ee9Sbenno  * Increases our "bufpos" appropriately.
45760a32ee9Sbenno  * This has no return value, but will assert() if the size of the buffer
45860a32ee9Sbenno  * is insufficient for the new data.
45960a32ee9Sbenno  */
46060a32ee9Sbenno void
io_buffer_buf(void * buf,size_t * bufpos,size_t buflen,const void * val,size_t valsz)461ba617adaSbenno io_buffer_buf(void *buf, size_t *bufpos, size_t buflen, const void *val,
462ba617adaSbenno     size_t valsz)
46360a32ee9Sbenno {
46460a32ee9Sbenno 
46560a32ee9Sbenno 	assert(*bufpos + valsz <= buflen);
46660a32ee9Sbenno 	memcpy(buf + *bufpos, val, valsz);
46760a32ee9Sbenno 	*bufpos += valsz;
46860a32ee9Sbenno }
46960a32ee9Sbenno 
47060a32ee9Sbenno /*
471*d9a51c35Sjmc  * Like io_buffer_buf(), but also accommodating for multiplexing codes.
472cf0de74fSflorian  * This should NEVER be passed to io_write_buf(), but instead passed
473cf0de74fSflorian  * directly to a write operation.
474cf0de74fSflorian  */
475cf0de74fSflorian void
io_lowbuffer_buf(struct sess * sess,void * buf,size_t * bufpos,size_t buflen,const void * val,size_t valsz)476cf0de74fSflorian io_lowbuffer_buf(struct sess *sess, void *buf,
477cf0de74fSflorian 	size_t *bufpos, size_t buflen, const void *val, size_t valsz)
478cf0de74fSflorian {
479cf0de74fSflorian 	int32_t	tagbuf;
480cf0de74fSflorian 
481ed5cc9fbSderaadt 	if (valsz == 0)
482cf0de74fSflorian 		return;
483cf0de74fSflorian 
484cf0de74fSflorian 	if (!sess->mplex_writes) {
485ba617adaSbenno 		io_buffer_buf(buf, bufpos, buflen, val, valsz);
486cf0de74fSflorian 		return;
487cf0de74fSflorian 	}
488cf0de74fSflorian 
489cf0de74fSflorian 	assert(*bufpos + valsz + sizeof(int32_t) <= buflen);
490cf0de74fSflorian 	assert(valsz == (valsz & 0xFFFFFF));
491cf0de74fSflorian 	tagbuf = htole32((7 << 24) + valsz);
492cf0de74fSflorian 
493ba617adaSbenno 	io_buffer_int(buf, bufpos, buflen, tagbuf);
494ba617adaSbenno 	io_buffer_buf(buf, bufpos, buflen, val, valsz);
495cf0de74fSflorian }
496cf0de74fSflorian 
497cf0de74fSflorian /*
498cf0de74fSflorian  * Allocate the space needed for io_lowbuffer_buf() and friends.
499cf0de74fSflorian  * This should be called for *each* lowbuffer operation, so:
500cf0de74fSflorian  *   io_lowbuffer_alloc(... sizeof(int32_t));
501cf0de74fSflorian  *   io_lowbuffer_int(...);
502cf0de74fSflorian  *   io_lowbuffer_alloc(... sizeof(int32_t));
503cf0de74fSflorian  *   io_lowbuffer_int(...);
504cf0de74fSflorian  * And not sizeof(int32_t) * 2 or whatnot.
505*d9a51c35Sjmc  * Returns zero on failure, non-zero on success.
506cf0de74fSflorian  */
507cf0de74fSflorian int
io_lowbuffer_alloc(struct sess * sess,void ** buf,size_t * bufsz,size_t * bufmax,size_t sz)508cf0de74fSflorian io_lowbuffer_alloc(struct sess *sess, void **buf,
509cf0de74fSflorian 	size_t *bufsz, size_t *bufmax, size_t sz)
510cf0de74fSflorian {
511cf0de74fSflorian 	void	*pp;
512cf0de74fSflorian 	size_t	 extra;
513cf0de74fSflorian 
514cf0de74fSflorian 	extra = sess->mplex_writes ? sizeof(int32_t) : 0;
515cf0de74fSflorian 
516cf0de74fSflorian 	if (*bufsz + sz + extra > *bufmax) {
517cf0de74fSflorian 		pp = realloc(*buf, *bufsz + sz + extra);
518cf0de74fSflorian 		if (pp == NULL) {
519b2a7eac7Sbenno 			ERR("realloc");
520cf0de74fSflorian 			return 0;
521cf0de74fSflorian 		}
522cf0de74fSflorian 		*buf = pp;
523cf0de74fSflorian 		*bufmax = *bufsz + sz + extra;
524cf0de74fSflorian 	}
525cf0de74fSflorian 	*bufsz += sz + extra;
526cf0de74fSflorian 	return 1;
527cf0de74fSflorian }
528cf0de74fSflorian 
529cf0de74fSflorian /*
530cf0de74fSflorian  * Like io_lowbuffer_buf(), but for a single integer.
531cf0de74fSflorian  */
532cf0de74fSflorian void
io_lowbuffer_int(struct sess * sess,void * buf,size_t * bufpos,size_t buflen,int32_t val)533cf0de74fSflorian io_lowbuffer_int(struct sess *sess, void *buf,
534cf0de74fSflorian 	size_t *bufpos, size_t buflen, int32_t val)
535cf0de74fSflorian {
536cf0de74fSflorian 	int32_t	nv = htole32(val);
537cf0de74fSflorian 
538cf0de74fSflorian 	io_lowbuffer_buf(sess, buf, bufpos, buflen, &nv, sizeof(int32_t));
539cf0de74fSflorian }
540cf0de74fSflorian 
541cf0de74fSflorian /*
542cf0de74fSflorian  * Like io_buffer_buf(), but for a single integer.
54360a32ee9Sbenno  */
54460a32ee9Sbenno void
io_buffer_int(void * buf,size_t * bufpos,size_t buflen,int32_t val)545ba617adaSbenno io_buffer_int(void *buf, size_t *bufpos, size_t buflen, int32_t val)
54660a32ee9Sbenno {
54760a32ee9Sbenno 	int32_t	nv = htole32(val);
54860a32ee9Sbenno 
549ba617adaSbenno 	io_buffer_buf(buf, bufpos, buflen, &nv, sizeof(int32_t));
55060a32ee9Sbenno }
55160a32ee9Sbenno 
552cf0de74fSflorian /*
553cf0de74fSflorian  * Like io_read_buf(), but for a long >=0.
554cf0de74fSflorian  * Returns zero on failure, non-zero on success.
555cf0de74fSflorian  */
55660a32ee9Sbenno int
io_read_long(struct sess * sess,int fd,int64_t * val)557aa1dcd86Sderaadt io_read_long(struct sess *sess, int fd, int64_t *val)
55860a32ee9Sbenno {
559aa1dcd86Sderaadt 	uint64_t	uoval;
56060a32ee9Sbenno 
561aa1dcd86Sderaadt 	if (!io_read_ulong(sess, fd, &uoval)) {
562b2a7eac7Sbenno 		ERRX1("io_read_long");
56360a32ee9Sbenno 		return 0;
56460a32ee9Sbenno 	}
565aa1dcd86Sderaadt 	*val = (int64_t)uoval;
566aa1dcd86Sderaadt 	if (*val < 0) {
567b2a7eac7Sbenno 		ERRX1("io_read_long negative");
568aa1dcd86Sderaadt 		return 0;
569aa1dcd86Sderaadt 	}
57060a32ee9Sbenno 	return 1;
57160a32ee9Sbenno }
57260a32ee9Sbenno 
573cf0de74fSflorian /*
574cf0de74fSflorian  * Like io_read_buf(), but for a long.
575cf0de74fSflorian  * Returns zero on failure, non-zero on success.
576cf0de74fSflorian  */
57760a32ee9Sbenno int
io_read_ulong(struct sess * sess,int fd,uint64_t * val)578aa1dcd86Sderaadt io_read_ulong(struct sess *sess, int fd, uint64_t *val)
57960a32ee9Sbenno {
580aa1dcd86Sderaadt 	uint64_t	 oval;
58160a32ee9Sbenno 	int32_t		 sval;
58260a32ee9Sbenno 
58360a32ee9Sbenno 	/* Start with the short-circuit: read as an int. */
58460a32ee9Sbenno 
58560a32ee9Sbenno 	if (!io_read_int(sess, fd, &sval)) {
586b2a7eac7Sbenno 		ERRX1("io_read_int");
58760a32ee9Sbenno 		return 0;
58840f74970Sclaudio 	}
58940f74970Sclaudio 	if (sval != -1) {
59040f74970Sclaudio 		*val = sval;
59160a32ee9Sbenno 		return 1;
59260a32ee9Sbenno 	}
59360a32ee9Sbenno 
594aa1dcd86Sderaadt 	/* If the int is -1, read as 64 bits. */
59560a32ee9Sbenno 
596aa1dcd86Sderaadt 	if (!io_read_buf(sess, fd, &oval, sizeof(uint64_t))) {
597b2a7eac7Sbenno 		ERRX1("io_read_buf");
59860a32ee9Sbenno 		return 0;
59960a32ee9Sbenno 	}
60060a32ee9Sbenno 
60160a32ee9Sbenno 	*val = le64toh(oval);
60260a32ee9Sbenno 	return 1;
60360a32ee9Sbenno }
60460a32ee9Sbenno 
60560a32ee9Sbenno /*
60660a32ee9Sbenno  * One thing we often need to do is read a size_t.
60760a32ee9Sbenno  * These are transmitted as int32_t, so make sure that the value
60860a32ee9Sbenno  * transmitted is not out of range.
60960a32ee9Sbenno  * FIXME: I assume that size_t can handle int32_t's max.
610cf0de74fSflorian  * Returns zero on failure, non-zero on success.
61160a32ee9Sbenno  */
61260a32ee9Sbenno int
io_read_size(struct sess * sess,int fd,size_t * val)61360a32ee9Sbenno io_read_size(struct sess *sess, int fd, size_t *val)
61460a32ee9Sbenno {
61560a32ee9Sbenno 	int32_t	oval;
61660a32ee9Sbenno 
61760a32ee9Sbenno 	if (!io_read_int(sess, fd, &oval)) {
618b2a7eac7Sbenno 		ERRX1("io_read_int");
61960a32ee9Sbenno 		return 0;
62060a32ee9Sbenno 	} else if (oval < 0) {
621b2a7eac7Sbenno 		ERRX("io_read_size: negative value");
62260a32ee9Sbenno 		return 0;
62360a32ee9Sbenno 	}
62460a32ee9Sbenno 
62560a32ee9Sbenno 	*val = oval;
62660a32ee9Sbenno 	return 1;
62760a32ee9Sbenno }
62860a32ee9Sbenno 
629cf0de74fSflorian /*
630cf0de74fSflorian  * Like io_read_buf(), but for an integer.
631cf0de74fSflorian  * Returns zero on failure, non-zero on success.
632cf0de74fSflorian  */
63360a32ee9Sbenno int
io_read_uint(struct sess * sess,int fd,uint32_t * val)634aa1dcd86Sderaadt io_read_uint(struct sess *sess, int fd, uint32_t *val)
63560a32ee9Sbenno {
636aa1dcd86Sderaadt 	uint32_t	oval;
63760a32ee9Sbenno 
638aa1dcd86Sderaadt 	if (!io_read_buf(sess, fd, &oval, sizeof(uint32_t))) {
639b2a7eac7Sbenno 		ERRX1("io_read_buf");
64060a32ee9Sbenno 		return 0;
64160a32ee9Sbenno 	}
64260a32ee9Sbenno 
64360a32ee9Sbenno 	*val = le32toh(oval);
64460a32ee9Sbenno 	return 1;
64560a32ee9Sbenno }
64660a32ee9Sbenno 
647aa1dcd86Sderaadt int
io_read_int(struct sess * sess,int fd,int32_t * val)648aa1dcd86Sderaadt io_read_int(struct sess *sess, int fd, int32_t *val)
649aa1dcd86Sderaadt {
650aa1dcd86Sderaadt 	return io_read_uint(sess, fd, (uint32_t *)val);
651aa1dcd86Sderaadt }
652aa1dcd86Sderaadt 
65360a32ee9Sbenno /*
65460a32ee9Sbenno  * Copies "valsz" from "buf", full size "bufsz" at position" bufpos",
65560a32ee9Sbenno  * into "val".
65660a32ee9Sbenno  * Calls assert() if the source doesn't have enough data.
65760a32ee9Sbenno  * Increases "bufpos" to the new position.
65860a32ee9Sbenno  */
65960a32ee9Sbenno void
io_unbuffer_buf(const void * buf,size_t * bufpos,size_t bufsz,void * val,size_t valsz)660ba617adaSbenno io_unbuffer_buf(const void *buf, size_t *bufpos, size_t bufsz, void *val,
661ba617adaSbenno     size_t valsz)
66260a32ee9Sbenno {
66360a32ee9Sbenno 
66460a32ee9Sbenno 	assert(*bufpos + valsz <= bufsz);
66560a32ee9Sbenno 	memcpy(val, buf + *bufpos, valsz);
66660a32ee9Sbenno 	*bufpos += valsz;
66760a32ee9Sbenno }
66860a32ee9Sbenno 
66960a32ee9Sbenno /*
670cf0de74fSflorian  * Calls io_unbuffer_buf() and converts.
67160a32ee9Sbenno  */
67260a32ee9Sbenno void
io_unbuffer_int(const void * buf,size_t * bufpos,size_t bufsz,int32_t * val)673ba617adaSbenno io_unbuffer_int(const void *buf, size_t *bufpos, size_t bufsz, int32_t *val)
67460a32ee9Sbenno {
67560a32ee9Sbenno 	int32_t	oval;
67660a32ee9Sbenno 
677ba617adaSbenno 	io_unbuffer_buf(buf, bufpos, bufsz, &oval, sizeof(int32_t));
67860a32ee9Sbenno 	*val = le32toh(oval);
67960a32ee9Sbenno }
68060a32ee9Sbenno 
681cf0de74fSflorian /*
682cf0de74fSflorian  * Calls io_unbuffer_buf() and converts.
683cf0de74fSflorian  */
68460a32ee9Sbenno int
io_unbuffer_size(const void * buf,size_t * bufpos,size_t bufsz,size_t * val)685ba617adaSbenno io_unbuffer_size(const void *buf, size_t *bufpos, size_t bufsz, size_t *val)
68660a32ee9Sbenno {
68760a32ee9Sbenno 	int32_t	oval;
68860a32ee9Sbenno 
689ba617adaSbenno 	io_unbuffer_int(buf, bufpos, bufsz, &oval);
69060a32ee9Sbenno 	if (oval < 0) {
691b2a7eac7Sbenno 		ERRX("io_unbuffer_size: negative value");
69260a32ee9Sbenno 		return 0;
69360a32ee9Sbenno 	}
69460a32ee9Sbenno 	*val = oval;
69560a32ee9Sbenno 	return 1;
69660a32ee9Sbenno }
69760a32ee9Sbenno 
698cf0de74fSflorian /*
699cf0de74fSflorian  * Like io_read_buf(), but for a single byte >=0.
700cf0de74fSflorian  * Returns zero on failure, non-zero on success.
701cf0de74fSflorian  */
70260a32ee9Sbenno int
io_read_byte(struct sess * sess,int fd,uint8_t * val)70360a32ee9Sbenno io_read_byte(struct sess *sess, int fd, uint8_t *val)
70460a32ee9Sbenno {
70560a32ee9Sbenno 
70660a32ee9Sbenno 	if (!io_read_buf(sess, fd, val, sizeof(uint8_t))) {
707b2a7eac7Sbenno 		ERRX1("io_read_buf");
70860a32ee9Sbenno 		return 0;
70960a32ee9Sbenno 	}
71060a32ee9Sbenno 	return 1;
71160a32ee9Sbenno }
71260a32ee9Sbenno 
713cf0de74fSflorian /*
714cf0de74fSflorian  * Like io_write_buf(), but for a single byte.
715cf0de74fSflorian  * Returns zero on failure, non-zero on success.
716cf0de74fSflorian  */
71760a32ee9Sbenno int
io_write_byte(struct sess * sess,int fd,uint8_t val)71860a32ee9Sbenno io_write_byte(struct sess *sess, int fd, uint8_t val)
71960a32ee9Sbenno {
72060a32ee9Sbenno 
72160a32ee9Sbenno 	if (!io_write_buf(sess, fd, &val, sizeof(uint8_t))) {
722b2a7eac7Sbenno 		ERRX1("io_write_buf");
72360a32ee9Sbenno 		return 0;
72460a32ee9Sbenno 	}
72560a32ee9Sbenno 	return 1;
72660a32ee9Sbenno }
727