Files
rsync/io.c
T
Andrew Tridgell 4d7c243ca6 daemon: bound the pre-transfer handshake with a timeout
rsyncd.conf(5) says of "timeout": "Using this parameter you can ensure that
rsync won't wait on a dead client forever."  That did not hold before a module
was known.  set_io_timeout() ran at the very end of rsync_module(), so the
greeting, authentication and the whole argument list were read with no I/O
timeout at all -- a peer could stall at any of them and the child waited
indefinitely.  Measured: 20 connections sending "@RSYNCD: 31.0" with no newline
were all still alive well past timeout=5, and only went away when the client
hung up.

The consequence is worse than an idle process.  claim_connection() runs BEFORE
auth_server(), so naming a module is enough to take a slot: an attacker with no
credentials could occupy every "max connections" slot of an authenticated
module and hold them for as long as it kept the sockets open, with the
documented control unable to recover them.  It costs the attacker nothing --
five stalled children measured 0 CPU ticks over 5s -- so this is descriptor and
slot exhaustion, not load.

Bound the handshake at min(configured, 60s).  "timeout" is a Locals parameter,
so lp_timeout(-1) reads the global section -- the same -1 idiom start_daemon()
already uses for lp_reverse_lookup().

Both halves of that minimum matter.  "timeout" DEFAULTS TO 0, so honouring only
the configured value would leave the daemon most exposed to this -- one whose
administrator never set a timeout -- exactly as pinnable as before.  And capping
matters because an operator who sets "timeout = 86400" for slow links is asking
for patience during a TRANSFER, not for a stranger to hold a pre-auth slot for a
day.  The pre-module phase has no legitimate reason to take even a minute.

The bound is retired the moment the module is known, which is what lets the
configured value still govern the transfer.  That retirement is load-bearing:
the per-module test only ever LOWERS the timeout (`lp_timeout(module_id) <
io_timeout`), so leaving the handshake bound in place would silently clamp a
module that asked for more -- "timeout = 300" would get 60.  It is cleared
before that test runs, and only when io_timeout is still the value we armed,
since the client's own --timeout is parsed in between and must win on its own
terms.  Verified: with no global timeout and "timeout = 120" in the module, a
connection idles past 75s rather than being dropped at 60.

Applied only for a real socket daemon (am_daemon > 0): an rsh-run daemon has no
listener to exhaust.

Verified end to end with max connections = 2 and timeout = 5: with two stalled
unauthenticated connections holding both slots, a legitimate client is refused
during the timeout window and served once it elapses.  Before this change it was
refused both times.

Reported by Chamal De Silva.  Not a regression -- 3.2.7 behaves the same way.

An idle timeout alone is not enough, which the review of the first version of
this change made concrete: safe_read() consults it only when poll() TIMES OUT,
so a peer sending a byte more often than allowed_lull (timeout/2) is never
checked at all.  Measured: one byte every 20s held the handshake open for 182s
against a 60s bound, keeping its max-connections slot the whole time -- the
reported attack, merely with the attacker typing.

Non-positive configured values are treated as "use the built-in bound":
"timeout" is parsed with atoi(), so "timeout = -1" would otherwise reach
set_io_timeout() (which reads it as no timeout) and alarm() (which would take it
as a huge unsigned count), disabling the very bound it looks like it configures.

The client's own --timeout is no longer inferred by comparing values, which could
not distinguish it from an identical armed value: io_timeout is zeroed before
parse_arguments(), so anything non-zero afterwards came from the client.

So the bound is absolute and lives in the READ PATH, next to the idle timeout
it complements: safe_read() caps each poll() at whatever is left of it and
gives up when it expires, so it is re-checked on every iteration and a peer
that keeps typing cannot outrun it.

It is deliberately NOT alarm()/SIGALRM.  Three earlier attempts used one and
each regressed something: fork() clears pending alarms, so the "post-xfer exec"
parent -- which waits for the ENTIRE transfer -- kept the deadline and _exit()ed
mid-transfer, skipping the hook and releasing the max-connections fcntl lock
while the transfer child ran on; "pre-xfer exec" and the name converter are
operator scripts that may legitimately outlast any handshake bound; and the
cancellation sat inside an exec-environment compile guard, so a build without
setenv/putenv kept it armed through the transfer.  A deadline consulted only
where the daemon is already blocked reading a peer has none of those hazards.

It is also kept entirely separate from io_timeout, which is an idle timeout the
module or client may set.  Mixing them clamped a module asking for more than the
bound ("timeout = 300" became 60) and leaked the handshake value into the
transfer.  Verified: module 300 stays 300, and a client --timeout=7 still wins.

Armed for each peer-driven phase and cleared between them: at the start of the
handshake, tightened by the module's own timeout once the module is known and
its slot claimed, cleared across the hook/fork setup, re-armed before
"@RSYNCD: OK" so it spans BOTH read_args() calls including secluded args, and
cleared before the transfer.

That argument-read coverage is the part that matters most.  auth_server()
returns immediately when a module sets no "auth users", so on an ANONYMOUS
module nothing is authenticated: without a bound there, a peer could claim the
slot, take the OK, and trickle an unterminated argument line forever.  Measured:
still open after 150s before, closed at 60s after.
2026-08-02 13:26:41 +10:00

2732 lines
76 KiB
C

/*
* Socket and pipe I/O utilities used in rsync.
*
* Copyright (C) 1996-2001 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
* Copyright (C) 2003-2022 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, visit the http://fsf.org website.
*/
/* Rsync provides its own multiplexing system, which is used to send
* stderr and stdout over a single socket.
*
* For historical reasons this is off during the start of the
* connection, but it's switched on quite early using
* io_start_multiplex_out() and io_start_multiplex_in(). */
#include "rsync.h"
#include "ifuncs.h"
#include "inums.h"
#include <poll.h>
/* Readiness bits we act on. poll() can report POLLERR/POLLHUP/POLLNVAL even
* when they were not requested, and POLLPRI stands in for select()'s old
* exception set. */
#define POLL_RD_BITS (POLLIN | POLLPRI | POLLERR | POLLHUP)
#define POLL_WR_BITS (POLLOUT | POLLERR | POLLHUP)
/** If no timeout is specified then use a 60 second I/O timeout */
#define SELECT_TIMEOUT 60
extern int bwlimit;
extern size_t bwlimit_writemax;
extern int io_timeout;
extern int am_server;
extern int am_sender;
extern int am_receiver;
extern int am_generator;
extern int local_server;
extern int msgs2stderr;
extern int inc_recurse;
extern int io_error;
extern int batch_fd;
extern int eol_nulls;
extern int flist_eof;
extern int file_total;
extern int file_old_total;
extern int list_only;
extern int read_batch;
extern int compat_flags;
extern int protect_args;
extern int checksum_seed;
extern int xfer_sum_len;
extern int daemon_connection;
extern int protocol_version;
extern int remove_source_files;
extern int write_batch;
extern int preserve_hard_links;
extern BOOL extra_flist_sending_enabled;
extern BOOL flush_ok_after_signal;
extern struct stats stats;
extern time_t stop_at_utime;
extern struct file_list *cur_flist;
#ifdef ICONV_OPTION
extern int filesfrom_convert;
extern iconv_t ic_send, ic_recv;
#endif
int csum_length = SHORT_SUM_LENGTH; /* initial value */
int allowed_lull = 0;
int msgdone_cnt = 0;
int forward_flist_data = 0;
BOOL flist_receiving_enabled = False;
/* Ignore an EOF error if non-zero. See whine_about_eof(). */
int kluge_around_eof = 0;
int got_kill_signal = -1; /* is set to 0 only after multiplexed I/O starts */
volatile sig_atomic_t got_sigusr2 = 0; /* set by the async-signal-safe SIGUSR2 handler */
int sock_f_in = -1;
int sock_f_out = -1;
int64 total_data_read = 0;
int64 total_data_written = 0;
char num_dev_ino_buf[4 + 8 + 8];
static struct {
xbuf in, out, msg;
int in_fd;
int out_fd; /* Both "out" and "msg" go to this fd. */
int in_multiplexed;
unsigned out_empty_len;
size_t raw_data_header_pos; /* in the out xbuf */
size_t raw_flushing_ends_before; /* in the out xbuf */
size_t raw_input_ends_before; /* in the in xbuf */
} iobuf = { .in_fd = -1, .out_fd = -1 };
static time_t last_io_in;
static time_t last_io_out;
/* Absolute wall-clock bound for peer-controlled daemon handshake reads.
* This is deliberately separate from io_timeout: the latter is an idle
* transfer timeout and may be supplied by the module or client. */
static time_t daemon_handshake_deadline;
static int write_batch_monitor_in = -1;
static int write_batch_monitor_out = -1;
static int ff_forward_fd = -1;
static int ff_reenable_multiplex = -1;
static char ff_lastchar = '\0';
static xbuf ff_xb = EMPTY_XBUF;
#ifdef ICONV_OPTION
static xbuf iconv_buf = EMPTY_XBUF;
#endif
static int select_timeout = SELECT_TIMEOUT;
/* Turn select_timeout (in seconds) into a poll() millisecond count, keeping it
* positive and bounded. A negative count means "wait forever" to poll(), which
* would bypass our keepalives and timeout enforcement entirely. */
static int poll_timeout_ms(void)
{
int secs = select_timeout;
if (secs <= 0 || secs > SELECT_TIMEOUT)
secs = SELECT_TIMEOUT;
return secs * 1000;
}
static int handshake_poll_timeout_ms(void)
{
time_t now, left;
int timeout = poll_timeout_ms();
if (!daemon_handshake_deadline)
return timeout;
now = time(NULL);
left = daemon_handshake_deadline - now;
if (left <= 0) {
rprintf(FERROR, "[%s] daemon handshake timeout -- exiting\n", who_am_i());
exit_cleanup(RERR_TIMEOUT);
}
if (left <= INT_MAX / 1000 && left * 1000 < timeout)
timeout = (int)left * 1000;
return timeout;
}
static int active_filecnt = 0;
static OFF_T active_bytecnt = 0;
static int first_message = 1;
static const char int_byte_extra[64] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (00 - 3F)/4 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* (40 - 7F)/4 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* (80 - BF)/4 */
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 6, /* (C0 - FF)/4 */
};
/* Our I/O buffers are sized with no bits on in the lowest byte of the "size"
* (indeed, our rounding of sizes in 1024-byte units assures more than this).
* This allows the code that is storing bytes near the physical end of a
* circular buffer to temporarily reduce the buffer's size (in order to make
* some storing idioms easier), while also making it simple to restore the
* buffer's actual size when the buffer's "pos" wraps around to the start (we
* just round the buffer's size up again). */
#define IOBUF_WAS_REDUCED(siz) ((siz) & 0xFF)
#define IOBUF_RESTORE_SIZE(siz) (((siz) | 0xFF) + 1)
#define IN_MULTIPLEXED (iobuf.in_multiplexed != 0)
#define IN_MULTIPLEXED_AND_READY (iobuf.in_multiplexed > 0)
#define OUT_MULTIPLEXED (iobuf.out_empty_len != 0)
#define PIO_NEED_INPUT (1<<0) /* The *_NEED_* flags are mutually exclusive. */
#define PIO_NEED_OUTROOM (1<<1)
#define PIO_NEED_MSGROOM (1<<2)
#define PIO_CONSUME_INPUT (1<<4) /* Must becombined with PIO_NEED_INPUT. */
#define PIO_INPUT_AND_CONSUME (PIO_NEED_INPUT | PIO_CONSUME_INPUT)
#define PIO_NEED_FLAGS (PIO_NEED_INPUT | PIO_NEED_OUTROOM | PIO_NEED_MSGROOM)
#define REMOTE_OPTION_ERROR "rsync: on remote machine: -"
#define REMOTE_OPTION_ERROR2 ": unknown option"
#define FILESFROM_BUFLEN 2048
enum festatus { FES_SUCCESS, FES_REDO, FES_NO_SEND };
static flist_ndx_list redo_list, hlink_list;
static void read_a_msg(void);
static void drain_multiplex_messages(void);
static void sleep_for_bwlimit(int bytes_written);
static void check_timeout(BOOL allow_keepalive, int keepalive_flags)
{
time_t t, chk;
/* On the receiving side, the generator is now the one that decides
* when a timeout has occurred. When it is sifting through a lot of
* files looking for work, it will be sending keep-alive messages to
* the sender, and even though the receiver won't be sending/receiving
* anything (not even keep-alive messages), the successful writes to
* the sender will keep things going. If the receiver is actively
* receiving data, it will ensure that the generator knows that it is
* not idle by sending the generator keep-alive messages (since the
* generator might be blocked trying to send checksums, it needs to
* know that the receiver is active). Thus, as long as one or the
* other is successfully doing work, the generator will not timeout. */
if (!io_timeout)
return;
t = time(NULL);
if (allow_keepalive) {
/* This may put data into iobuf.msg w/o flushing. */
maybe_send_keepalive(t, keepalive_flags);
}
if (!last_io_in)
last_io_in = t;
if (am_receiver)
return;
chk = MAX(last_io_out, last_io_in);
if (t - chk >= io_timeout) {
if (am_server)
msgs2stderr = 1;
rprintf(FERROR, "[%s] io timeout after %d seconds -- exiting\n",
who_am_i(), (int)(t-chk));
exit_cleanup(RERR_TIMEOUT);
}
}
/* It's almost always an error to get an EOF when we're trying to read from the
* network, because the protocol is (for the most part) self-terminating.
*
* There is one case for the receiver when it is at the end of the transfer
* (hanging around reading any keep-alive packets that might come its way): if
* the sender dies before the generator's kill-signal comes through, we can end
* up here needing to loop until the kill-signal arrives. In this situation,
* kluge_around_eof will be < 0.
*
* There is another case for older protocol versions (< 24) where the module
* listing was not terminated, so we must ignore an EOF error in that case and
* exit. In this situation, kluge_around_eof will be > 0. */
static NORETURN void whine_about_eof(BOOL allow_kluge)
{
if (kluge_around_eof && allow_kluge) {
int i;
if (kluge_around_eof > 0)
exit_cleanup(0);
/* The receiver is waiting here for the generator's SIGUSR2; act on it
* (exit cleanly) the moment it arrives rather than sleeping the full
* 10s and then erroring. The async-signal-safe handler only sets the
* flag, so this loop must poll it. */
for (i = 10*1000/20; i--; ) {
if (got_sigusr2)
receive_sigusr2();
msleep(20);
}
}
rprintf(FERROR, RSYNC_NAME ": connection unexpectedly closed "
"(%s bytes received so far) [%s]\n",
big_num(stats.total_read), who_am_i());
exit_cleanup(RERR_STREAMIO);
}
/* Do a safe read, handling any needed looping and error handling.
* Returns the count of the bytes read, which will only be different
* from "len" if we encountered an EOF. This routine is not used on
* the socket except very early in the transfer. */
static size_t safe_read(int fd, char *buf, size_t len)
{
size_t got = 0;
assert(fd != iobuf.in_fd);
while (1) {
struct pollfd pfd;
int cnt;
if (got_sigusr2) /* receiver told to wrap up (e.g. a --read-batch fd) */
receive_sigusr2();
/* We use poll() rather than select() so that a high-numbered fd
* (>= FD_SETSIZE) cannot overflow an fd_set bitmap. */
pfd.fd = fd;
pfd.events = POLLIN | POLLPRI;
pfd.revents = 0;
cnt = poll(&pfd, 1, handshake_poll_timeout_ms());
if (cnt <= 0) {
if (cnt < 0 && errno != EINTR && errno != EAGAIN) {
rsyserr(FERROR, errno, "safe_read poll failed");
exit_cleanup(RERR_FILEIO);
}
check_timeout(1, MSK_ALLOW_FLUSH);
continue;
}
/* An invalid fd is reported here rather than via poll()'s return. */
if (pfd.revents & POLLNVAL) {
rsyserr(FERROR, EBADF, "safe_read poll failed");
exit_cleanup(RERR_FILEIO);
}
if (pfd.revents & POLL_RD_BITS) {
ssize_t n = read(fd, buf + got, len - got);
if (DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] safe_read(%d)=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), fd, (SIZE_T_FMT_CAST)n);
}
if (n == 0)
break;
if (n < 0) {
if (errno == EINTR)
continue;
rsyserr(FERROR, errno, "safe_read failed to read %" SIZE_T_FMT_MOD "d bytes",
(SIZE_T_FMT_CAST)len);
exit_cleanup(RERR_STREAMIO);
}
if ((got += (size_t)n) == len)
break;
}
}
return got;
}
static const char *what_fd_is(int fd)
{
static char buf[20];
if (fd == sock_f_out)
return "socket";
else if (fd == iobuf.out_fd)
return "message fd";
else if (fd == batch_fd)
return "batch file";
else {
snprintf(buf, sizeof buf, "fd %d", fd);
return buf;
}
}
/* Do a safe write, handling any needed looping and error handling.
* Returns only if everything was successfully written. This routine
* is not used on the socket except very early in the transfer. */
static void safe_write(int fd, const char *buf, size_t len)
{
ssize_t n;
assert(fd != iobuf.out_fd);
if (got_sigusr2) /* receiver told to wrap up before this (batch) write */
receive_sigusr2();
n = write(fd, buf, len);
if ((size_t)n == len)
return;
if (n < 0) {
if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN) {
write_failed:
rsyserr(FERROR, errno,
"safe_write failed to write %" SIZE_T_FMT_MOD "d bytes to %s",
(SIZE_T_FMT_CAST)len, what_fd_is(fd));
exit_cleanup(RERR_STREAMIO);
}
} else {
buf += n;
len -= n;
}
while (len) {
struct pollfd pfd;
int cnt;
if (got_sigusr2) /* receiver told to wrap up (e.g. a --write-batch fd) */
receive_sigusr2();
/* poll() avoids the FD_SETSIZE limit that select() imposes. */
pfd.fd = fd;
pfd.events = POLLOUT;
pfd.revents = 0;
cnt = poll(&pfd, 1, poll_timeout_ms());
if (cnt <= 0) {
if (cnt < 0 && errno != EINTR && errno != EAGAIN) {
rsyserr(FERROR, errno, "safe_write poll failed on %s", what_fd_is(fd));
exit_cleanup(RERR_FILEIO);
}
if (io_timeout)
maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
continue;
}
if (pfd.revents & POLLNVAL) {
rsyserr(FERROR, EBADF, "safe_write poll failed on %s", what_fd_is(fd));
exit_cleanup(RERR_FILEIO);
}
if (pfd.revents & POLL_WR_BITS) {
n = write(fd, buf, len);
if (n < 0) {
if (errno == EINTR)
continue;
goto write_failed;
}
buf += n;
len -= n;
}
}
}
/* This is only called when files-from data is known to be available. We read
* a chunk of data and put it into the output buffer. */
static void forward_filesfrom_data(void)
{
ssize_t len;
len = read(ff_forward_fd, ff_xb.buf + ff_xb.len, ff_xb.size - ff_xb.len);
if (len <= 0) {
if (len == 0 || errno != EINTR) {
/* Send end-of-file marker */
ff_forward_fd = -1;
write_buf(iobuf.out_fd, "\0\0", ff_lastchar ? 2 : 1);
free_xbuf(&ff_xb);
if (ff_reenable_multiplex >= 0)
io_start_multiplex_out(ff_reenable_multiplex);
free_implied_include_partial_string();
}
return;
}
if (DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] files-from read=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), (SIZE_T_FMT_CAST)len);
}
#ifdef ICONV_OPTION
len += ff_xb.len;
#endif
if (!eol_nulls) {
char *s = ff_xb.buf + len;
/* Transform CR and/or LF into '\0' */
while (s-- > ff_xb.buf) {
if (*s == '\n' || *s == '\r')
*s = '\0';
}
}
if (ff_lastchar)
ff_xb.pos = 0;
else {
char *s = ff_xb.buf;
/* Last buf ended with a '\0', so don't let this buf start with one. */
while (len && *s == '\0')
s++, len--;
ff_xb.pos = s - ff_xb.buf;
}
#ifdef ICONV_OPTION
if (filesfrom_convert && len) {
char *sob = ff_xb.buf + ff_xb.pos, *s = sob;
char *eob = sob + len;
int flags = ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_CIRCULAR_OUT;
if (ff_lastchar == '\0')
flags |= ICB_INIT;
/* Convert/send each null-terminated string separately, skipping empties. */
while (s != eob) {
if (*s++ == '\0') {
ff_xb.len = s - sob - 1;
add_implied_include(sob, 0);
if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0)
exit_cleanup(RERR_PROTOCOL); /* impossible? */
write_buf(iobuf.out_fd, s-1, 1); /* Send the '\0'. */
while (s != eob && *s == '\0')
s++;
sob = s;
ff_xb.pos = sob - ff_xb.buf;
flags |= ICB_INIT;
}
}
if ((ff_xb.len = s - sob) == 0)
ff_lastchar = '\0';
else {
/* Handle a partial string specially, saving any incomplete chars. */
implied_include_partial_string(sob, s);
flags &= ~ICB_INCLUDE_INCOMPLETE;
if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0) {
if (errno == E2BIG)
exit_cleanup(RERR_PROTOCOL); /* impossible? */
if (ff_xb.pos)
memmove(ff_xb.buf, ff_xb.buf + ff_xb.pos, ff_xb.len);
}
ff_lastchar = 'x'; /* Anything non-zero. */
}
} else
#endif
if (len) {
char *f = ff_xb.buf + ff_xb.pos;
char *t = ff_xb.buf;
char *eob = f + len;
char *cur = t;
/* Eliminate any multi-'\0' runs. */
while (f != eob) {
if (!(*t++ = *f++)) {
add_implied_include(cur, 0);
cur = t;
while (f != eob && *f == '\0')
f++;
}
}
implied_include_partial_string(cur, t);
ff_lastchar = f[-1];
if ((len = t - ff_xb.buf) != 0) {
/* This will not circle back to perform_io() because we only get
* called when there is plenty of room in the output buffer. */
write_buf(iobuf.out_fd, ff_xb.buf, len);
}
}
}
void reduce_iobuf_size(xbuf *out, size_t new_size)
{
if (new_size < out->size) {
/* Avoid weird buffer interactions by only outputting this to stderr. */
if (msgs2stderr == 1 && DEBUG_GTE(IO, 4)) {
const char *name = out == &iobuf.out ? "iobuf.out"
: out == &iobuf.msg ? "iobuf.msg"
: NULL;
if (name) {
rprintf(FINFO, "[%s] reduced size of %s (-%d)\n",
who_am_i(), name, (int)(out->size - new_size));
}
}
out->size = new_size;
}
}
void restore_iobuf_size(xbuf *out)
{
if (IOBUF_WAS_REDUCED(out->size)) {
size_t new_size = IOBUF_RESTORE_SIZE(out->size);
/* Avoid weird buffer interactions by only outputting this to stderr. */
if (msgs2stderr == 1 && DEBUG_GTE(IO, 4)) {
const char *name = out == &iobuf.out ? "iobuf.out"
: out == &iobuf.msg ? "iobuf.msg"
: NULL;
if (name) {
rprintf(FINFO, "[%s] restored size of %s (+%d)\n",
who_am_i(), name, (int)(new_size - out->size));
}
}
out->size = new_size;
}
}
static void handle_kill_signal(BOOL flush_ok)
{
got_kill_signal = -1;
flush_ok_after_signal = flush_ok;
exit_cleanup(RERR_SIGNAL);
}
/* Perform buffered input and/or output until specified conditions are met.
* When given a "needed" read or write request, this returns without doing any
* I/O if the needed input bytes or write space is already available. Once I/O
* is needed, this will try to do whatever reading and/or writing is currently
* possible, up to the maximum buffer allowances, no matter if this is a read
* or write request. However, the I/O stops as soon as the required input
* bytes or output space is available. If this is not a read request, the
* routine may also do some advantageous reading of messages from a multiplexed
* input source (which ensures that we don't jam up with everyone in their
* "need to write" code and nobody reading the accumulated data that would make
* writing possible).
*
* The iobuf.in, .out and .msg buffers are all circular. Callers need to be
* aware that some data copies will need to be split when the bytes wrap around
* from the end to the start. In order to help make writing into the output
* buffers easier for some operations (such as the use of SIVAL() into the
* buffer) a buffer may be temporarily shortened by a small amount, but the
* original size will be automatically restored when the .pos wraps to the
* start. See also the 3 raw_* iobuf vars that are used in the handling of
* MSG_DATA bytes as they are read-from/written-into the buffers.
*
* When writing, we flush data in the following priority order:
*
* 1. Finish writing any in-progress MSG_DATA sequence from iobuf.out.
*
* 2. Write out all the messages from the message buf (if iobuf.msg is active).
* Yes, this means that a PIO_NEED_OUTROOM call will completely flush any
* messages before getting to the iobuf.out flushing (except for rule 1).
*
* 3. Write out the raw data from iobuf.out, possibly filling in the multiplexed
* MSG_DATA header that was pre-allocated (when output is multiplexed).
*
* TODO: items for possible future work:
*
* - Make this routine able to read the generator-to-receiver batch flow?
*
* Unlike the old routines that this replaces, it is OK to read ahead as far as
* we can because the read_a_msg() routine now reads its bytes out of the input
* buffer. In the old days, only raw data was in the input buffer, and any
* unused raw data in the buf would prevent the reading of socket data. */
static char *perform_io(size_t needed, int flags)
{
struct pollfd pfds[3];
int cnt, max_fd, npfds, poll_timeout, in_pollpos, out_pollpos, ff_pollpos;
size_t empty_buf_len = 0;
xbuf *out;
char *data;
if (iobuf.in.len == 0 && iobuf.in.pos != 0) {
if (iobuf.raw_input_ends_before)
iobuf.raw_input_ends_before -= iobuf.in.pos;
iobuf.in.pos = 0;
}
switch (flags & PIO_NEED_FLAGS) {
case PIO_NEED_INPUT:
/* We never resize the circular input buffer. */
if (iobuf.in.size < needed) {
rprintf(FERROR, "need to read %" SIZE_T_FMT_MOD "d bytes,"
" iobuf.in.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
(SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)iobuf.in.size);
exit_cleanup(RERR_PROTOCOL);
}
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d, %sinput)\n",
who_am_i(), (SIZE_T_FMT_CAST)needed, flags & PIO_CONSUME_INPUT ? "consume&" : "");
}
break;
case PIO_NEED_OUTROOM:
/* We never resize the circular output buffer. */
if (iobuf.out.size - iobuf.out_empty_len < needed) {
fprintf(stderr, "need to write %" SIZE_T_FMT_MOD "d bytes,"
" iobuf.out.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
(SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)(iobuf.out.size - iobuf.out_empty_len));
exit_cleanup(RERR_PROTOCOL);
}
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d,"
" outroom) needs to flush %" SIZE_T_FMT_MOD "d\n",
who_am_i(), (SIZE_T_FMT_CAST)needed,
iobuf.out.len + needed > iobuf.out.size
? (SIZE_T_FMT_CAST)(iobuf.out.len + needed - iobuf.out.size) : (SIZE_T_FMT_CAST)0);
}
break;
case PIO_NEED_MSGROOM:
/* We never resize the circular message buffer. */
if (iobuf.msg.size < needed) {
fprintf(stderr, "need to write %" SIZE_T_FMT_MOD "d bytes,"
" iobuf.msg.buf is only %" SIZE_T_FMT_MOD "d bytes.\n",
(SIZE_T_FMT_CAST)needed, (SIZE_T_FMT_CAST)iobuf.msg.size);
exit_cleanup(RERR_PROTOCOL);
}
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d,"
" msgroom) needs to flush %" SIZE_T_FMT_MOD "d\n",
who_am_i(), (SIZE_T_FMT_CAST)needed,
iobuf.msg.len + needed > iobuf.msg.size
? (SIZE_T_FMT_CAST)(iobuf.msg.len + needed - iobuf.msg.size) : (SIZE_T_FMT_CAST)0);
}
break;
case 0:
if (msgs2stderr == 1 && DEBUG_GTE(IO, 3)) {
rprintf(FINFO, "[%s] perform_io(%" SIZE_T_FMT_MOD "d, %d)\n",
who_am_i(), (SIZE_T_FMT_CAST)needed, flags);
}
break;
default:
exit_cleanup(RERR_UNSUPPORTED);
}
while (1) {
switch (flags & PIO_NEED_FLAGS) {
case PIO_NEED_INPUT:
if (iobuf.in.len >= needed)
goto double_break;
break;
case PIO_NEED_OUTROOM:
/* Note that iobuf.out_empty_len doesn't factor into this check
* because iobuf.out.len already holds any needed header len. */
if (iobuf.out.len + needed <= iobuf.out.size)
goto double_break;
break;
case PIO_NEED_MSGROOM:
if (iobuf.msg.len + needed <= iobuf.msg.size)
goto double_break;
break;
}
max_fd = -1;
npfds = 0;
in_pollpos = out_pollpos = ff_pollpos = -1;
if (iobuf.in_fd >= 0 && iobuf.in.size - iobuf.in.len) {
if (!read_batch || batch_fd >= 0) {
pfds[npfds].fd = iobuf.in_fd;
pfds[npfds].events = POLLIN | POLLPRI;
pfds[npfds].revents = 0;
in_pollpos = npfds++;
}
if (iobuf.in_fd > max_fd)
max_fd = iobuf.in_fd;
}
/* Only do more filesfrom processing if there is enough room in the out buffer. */
if (ff_forward_fd >= 0 && iobuf.out.size - iobuf.out.len > FILESFROM_BUFLEN*2) {
pfds[npfds].fd = ff_forward_fd;
pfds[npfds].events = POLLIN;
pfds[npfds].revents = 0;
ff_pollpos = npfds++;
if (ff_forward_fd > max_fd)
max_fd = ff_forward_fd;
}
if (iobuf.out_fd >= 0) {
if (iobuf.raw_flushing_ends_before
|| (!iobuf.msg.len && iobuf.out.len > iobuf.out_empty_len && !(flags & PIO_NEED_MSGROOM))) {
if (OUT_MULTIPLEXED && !iobuf.raw_flushing_ends_before) {
/* The iobuf.raw_flushing_ends_before value can point off the end
* of the iobuf.out buffer for a while, for easier subtracting. */
iobuf.raw_flushing_ends_before = iobuf.out.pos + iobuf.out.len;
SIVAL(iobuf.out.buf + iobuf.raw_data_header_pos, 0,
((MPLEX_BASE + (int)MSG_DATA)<<24) + iobuf.out.len - 4);
if (msgs2stderr == 1 && DEBUG_GTE(IO, 1)) {
rprintf(FINFO, "[%s] send_msg(%d, %" SIZE_T_FMT_MOD "d)\n",
who_am_i(), (int)MSG_DATA, (SIZE_T_FMT_CAST)iobuf.out.len - 4);
}
/* reserve room for the next MSG_DATA header */
iobuf.raw_data_header_pos = iobuf.raw_flushing_ends_before;
if (iobuf.raw_data_header_pos >= iobuf.out.size)
iobuf.raw_data_header_pos -= iobuf.out.size;
else if (iobuf.raw_data_header_pos + 4 > iobuf.out.size) {
/* The 4-byte header won't fit at the end of the buffer,
* so we'll temporarily reduce the output buffer's size
* and put the header at the start of the buffer. */
reduce_iobuf_size(&iobuf.out, iobuf.raw_data_header_pos);
iobuf.raw_data_header_pos = 0;
}
/* Yes, it is possible for this to make len > size for a while. */
iobuf.out.len += 4;
}
empty_buf_len = iobuf.out_empty_len;
out = &iobuf.out;
} else if (iobuf.msg.len) {
empty_buf_len = 0;
out = &iobuf.msg;
} else
out = NULL;
if (out) {
/* A direct daemon connection uses one fd for both
* directions; give it a single row with both events
* rather than two rows carrying different masks. */
if (in_pollpos >= 0 && iobuf.out_fd == iobuf.in_fd) {
pfds[in_pollpos].events |= POLLOUT;
out_pollpos = in_pollpos;
} else {
pfds[npfds].fd = iobuf.out_fd;
pfds[npfds].events = POLLOUT;
pfds[npfds].revents = 0;
out_pollpos = npfds++;
}
if (iobuf.out_fd > max_fd)
max_fd = iobuf.out_fd;
}
} else
out = NULL;
if (max_fd < 0) {
switch (flags & PIO_NEED_FLAGS) {
case PIO_NEED_INPUT:
iobuf.in.len = 0;
if (kluge_around_eof == 2)
exit_cleanup(0);
if (iobuf.in_fd == -2)
whine_about_eof(True);
rprintf(FERROR, "error in perform_io: no fd for input.\n");
exit_cleanup(RERR_PROTOCOL);
case PIO_NEED_OUTROOM:
case PIO_NEED_MSGROOM:
msgs2stderr = 1;
drain_multiplex_messages();
if (iobuf.out_fd == -2)
whine_about_eof(True);
rprintf(FERROR, "error in perform_io: no fd for output.\n");
exit_cleanup(RERR_PROTOCOL);
default:
/* No stated needs, so I guess this is OK. */
break;
}
break;
}
if (got_kill_signal > 0)
handle_kill_signal(True);
if (got_sigusr2)
receive_sigusr2();
if (extra_flist_sending_enabled) {
if (file_total - file_old_total < MAX_FILECNT_LOOKAHEAD && IN_MULTIPLEXED_AND_READY)
poll_timeout = 0;
else {
extra_flist_sending_enabled = False;
poll_timeout = poll_timeout_ms();
}
} else
poll_timeout = poll_timeout_ms();
cnt = poll(pfds, npfds, poll_timeout);
if (cnt <= 0) {
if (cnt < 0 && errno == EBADF) {
msgs2stderr = 1;
exit_cleanup(RERR_SOCKETIO);
}
if (extra_flist_sending_enabled) {
extra_flist_sending_enabled = False;
send_extra_file_list(sock_f_out, -1);
extra_flist_sending_enabled = !flist_eof;
} else
check_timeout((flags & PIO_NEED_INPUT) != 0, 0);
/* Just in case... */
if (in_pollpos >= 0)
pfds[in_pollpos].revents = 0;
if (ff_pollpos >= 0)
pfds[ff_pollpos].revents = 0;
if (out_pollpos >= 0)
pfds[out_pollpos].revents = 0;
}
if (cnt > 0) {
/* poll() reports a bad fd here, not via its return value. */
int p;
for (p = 0; p < npfds; p++) {
if (pfds[p].revents & POLLNVAL) {
msgs2stderr = 1;
rsyserr(FERROR, EBADF, "perform_io: poll reported an invalid fd");
exit_cleanup(RERR_SOCKETIO);
}
}
}
if (iobuf.in_fd >= 0 && in_pollpos >= 0
&& pfds[in_pollpos].revents & POLL_RD_BITS) {
size_t len, pos = iobuf.in.pos + iobuf.in.len;
ssize_t n;
if (pos >= iobuf.in.size) {
pos -= iobuf.in.size;
len = iobuf.in.size - iobuf.in.len;
} else
len = iobuf.in.size - pos;
if ((n = read(iobuf.in_fd, iobuf.in.buf + pos, len)) <= 0) {
if (n == 0) {
/* Signal that input has become invalid. */
if (!read_batch || batch_fd < 0 || am_generator)
iobuf.in_fd = -2;
batch_fd = -1;
continue;
}
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
n = 0;
else {
/* Don't write errors on a dead socket. */
if (iobuf.in_fd == sock_f_in) {
if (am_sender)
msgs2stderr = 1;
rsyserr(FERROR_SOCKET, errno, "read error");
} else
rsyserr(FERROR, errno, "read error");
exit_cleanup(RERR_SOCKETIO);
}
}
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] recv=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), (SIZE_T_FMT_CAST)n);
}
if (io_timeout) {
last_io_in = time(NULL);
if (io_timeout && flags & PIO_NEED_INPUT)
maybe_send_keepalive(last_io_in, 0);
}
stats.total_read += n;
iobuf.in.len += n;
}
if (stop_at_utime && time(NULL) >= stop_at_utime) {
rprintf(FERROR, "stopping at requested limit\n");
exit_cleanup(RERR_TIMEOUT);
}
if (out && out_pollpos >= 0 && pfds[out_pollpos].revents & POLL_WR_BITS) {
size_t len = iobuf.raw_flushing_ends_before ? iobuf.raw_flushing_ends_before - out->pos : out->len;
ssize_t n;
if (bwlimit_writemax && len > bwlimit_writemax)
len = bwlimit_writemax;
if (out->pos + len > out->size)
len = out->size - out->pos;
if ((n = write(iobuf.out_fd, out->buf + out->pos, len)) <= 0) {
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
n = 0;
else {
/* Don't write errors on a dead socket. */
msgs2stderr = 1;
iobuf.out_fd = -2;
iobuf.out.len = iobuf.msg.len = iobuf.raw_flushing_ends_before = 0;
rsyserr(FERROR_SOCKET, errno, "write error");
drain_multiplex_messages();
exit_cleanup(RERR_SOCKETIO);
}
}
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] %s sent=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), out == &iobuf.out ? "out" : "msg", (SIZE_T_FMT_CAST)n);
}
if (io_timeout)
last_io_out = time(NULL);
stats.total_written += n;
if (bwlimit_writemax)
sleep_for_bwlimit(n);
if ((out->pos += n) == out->size) {
if (iobuf.raw_flushing_ends_before)
iobuf.raw_flushing_ends_before -= out->size;
out->pos = 0;
restore_iobuf_size(out);
} else if (out->pos == iobuf.raw_flushing_ends_before)
iobuf.raw_flushing_ends_before = 0;
if ((out->len -= n) == empty_buf_len) {
out->pos = 0;
restore_iobuf_size(out);
if (empty_buf_len)
iobuf.raw_data_header_pos = 0;
}
}
if (got_kill_signal > 0)
handle_kill_signal(True);
if (got_sigusr2)
receive_sigusr2();
/* We need to help prevent deadlock by doing what reading
* we can whenever we are here trying to write. */
if (IN_MULTIPLEXED_AND_READY && !(flags & PIO_NEED_INPUT)) {
while (!iobuf.raw_input_ends_before && iobuf.in.len > 512)
read_a_msg();
if (flist_receiving_enabled && iobuf.in.len > 512)
wait_for_receiver(); /* generator only */
}
if (ff_forward_fd >= 0 && ff_pollpos >= 0
&& pfds[ff_pollpos].revents & POLL_RD_BITS) {
/* This can potentially flush all output and enable
* multiplexed output, so keep this last in the loop
* and be sure to not cache anything that would break
* such a change. */
forward_filesfrom_data();
}
}
double_break:
if (got_kill_signal > 0)
handle_kill_signal(True);
if (got_sigusr2)
receive_sigusr2();
data = iobuf.in.buf + iobuf.in.pos;
if (flags & PIO_CONSUME_INPUT) {
iobuf.in.len -= needed;
iobuf.in.pos += needed;
if (iobuf.in.pos == iobuf.raw_input_ends_before)
iobuf.raw_input_ends_before = 0;
if (iobuf.in.pos >= iobuf.in.size) {
iobuf.in.pos -= iobuf.in.size;
if (iobuf.raw_input_ends_before)
iobuf.raw_input_ends_before -= iobuf.in.size;
}
}
return data;
}
static void raw_read_buf(char *buf, size_t len)
{
size_t pos = iobuf.in.pos;
char *data = perform_io(len, PIO_INPUT_AND_CONSUME);
if (iobuf.in.pos <= pos && len) {
size_t siz = len - iobuf.in.pos;
memcpy(buf, data, siz);
memcpy(buf + siz, iobuf.in.buf, iobuf.in.pos);
} else
memcpy(buf, data, len);
}
static int32 raw_read_int(void)
{
char *data, buf[4];
if (iobuf.in.size - iobuf.in.pos >= 4)
data = perform_io(4, PIO_INPUT_AND_CONSUME);
else
raw_read_buf(data = buf, 4);
return IVAL(data, 0);
}
void noop_io_until_death(void)
{
char buf[1024];
if (!iobuf.in.buf || !iobuf.out.buf || iobuf.in_fd < 0 || iobuf.out_fd < 0 || kluge_around_eof)
return;
/* If we're talking to a daemon over a socket, don't short-circuit this logic */
if (msgs2stderr && daemon_connection >= 0)
return;
kluge_around_eof = 2;
/* Setting an I/O timeout ensures that if something inexplicably weird
* happens, we won't hang around forever. */
if (!io_timeout)
set_io_timeout(60);
while (1)
read_buf(iobuf.in_fd, buf, sizeof buf);
}
/* Buffer a message for the multiplexed output stream. Is not used for (normal) MSG_DATA. */
int send_msg(enum msgcode code, const char *buf, size_t len, int convert)
{
char *hdr;
size_t needed, pos;
BOOL want_debug = DEBUG_GTE(IO, 1) && convert >= 0 && (msgs2stderr == 1 || code != MSG_INFO);
if (!OUT_MULTIPLEXED)
return 0;
if (want_debug) {
rprintf(FINFO, "[%s] send_msg(%d, %" SIZE_T_FMT_MOD "d)\n",
who_am_i(), (int)code, (SIZE_T_FMT_CAST)len);
}
/* When checking for enough free space for this message, we need to
* make sure that there is space for the 4-byte header, plus we'll
* assume that we may waste up to 3 bytes (if the header doesn't fit
* at the physical end of the buffer). */
#ifdef ICONV_OPTION
if (convert > 0 && ic_send == (iconv_t)-1)
convert = 0;
if (convert > 0) {
/* Ensuring double-size room leaves space for maximal conversion expansion. */
needed = len*2 + 4 + 3;
} else
#endif
needed = len + 4 + 3;
if (iobuf.msg.len + needed > iobuf.msg.size) {
if (am_sender)
perform_io(needed, PIO_NEED_MSGROOM);
else { /* We sometimes allow the iobuf.msg size to increase to avoid a deadlock. */
size_t old_size = iobuf.msg.size;
restore_iobuf_size(&iobuf.msg);
realloc_xbuf(&iobuf.msg, iobuf.msg.size * 2);
if (iobuf.msg.pos + iobuf.msg.len > old_size)
memcpy(iobuf.msg.buf + old_size, iobuf.msg.buf, iobuf.msg.pos + iobuf.msg.len - old_size);
}
}
pos = iobuf.msg.pos + iobuf.msg.len; /* Must be set after any flushing. */
if (pos >= iobuf.msg.size)
pos -= iobuf.msg.size;
else if (pos + 4 > iobuf.msg.size) {
/* The 4-byte header won't fit at the end of the buffer,
* so we'll temporarily reduce the message buffer's size
* and put the header at the start of the buffer. */
reduce_iobuf_size(&iobuf.msg, pos);
pos = 0;
}
hdr = iobuf.msg.buf + pos;
iobuf.msg.len += 4; /* Allocate room for the coming header bytes. */
#ifdef ICONV_OPTION
if (convert > 0) {
xbuf inbuf;
INIT_XBUF(inbuf, (char*)buf, len, (size_t)-1);
len = iobuf.msg.len;
iconvbufs(ic_send, &inbuf, &iobuf.msg,
ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_CIRCULAR_OUT | ICB_INIT);
if (inbuf.len > 0) {
rprintf(FERROR, "overflowed iobuf.msg buffer in send_msg");
exit_cleanup(RERR_UNSUPPORTED);
}
len = iobuf.msg.len - len;
} else
#endif
{
size_t siz;
if ((pos += 4) == iobuf.msg.size)
pos = 0;
/* Handle a split copy if we wrap around the end of the circular buffer. */
if (pos >= iobuf.msg.pos && (siz = iobuf.msg.size - pos) < len) {
memcpy(iobuf.msg.buf + pos, buf, siz);
memcpy(iobuf.msg.buf, buf + siz, len - siz);
} else
memcpy(iobuf.msg.buf + pos, buf, len);
iobuf.msg.len += len;
}
SIVAL(hdr, 0, ((MPLEX_BASE + (int)code)<<24) + len);
if (want_debug && convert > 0) {
rprintf(FINFO, "[%s] converted msg len=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), (SIZE_T_FMT_CAST)len);
}
return 1;
}
void send_msg_int(enum msgcode code, int num)
{
char numbuf[4];
if (DEBUG_GTE(IO, 1))
rprintf(FINFO, "[%s] send_msg_int(%d, %d)\n", who_am_i(), (int)code, num);
SIVAL(numbuf, 0, num);
send_msg(code, numbuf, 4, -1);
}
void send_msg_success(const char *fname, int num)
{
/* Batch-only mode has not duplicated anything on the receiving side yet.
* The receiver still reports success to the generator for file-list and
* hard-link bookkeeping, but the generator must not turn that status into
* sender-side removal. */
if (am_generator && write_batch < 0 && remove_source_files)
return;
if (local_server) {
STRUCT_STAT st;
if (DEBUG_GTE(IO, 1))
rprintf(FINFO, "[%s] send_msg_success(%d)\n", who_am_i(), num);
/* The dev/ino is consumed only by the sender's --remove-source-files
* same-file safety check (successful_send), so skip the per-file
* stat entirely otherwise -- it's sent but never read. */
if (remove_source_files && stat(fname, &st) == 0) {
SIVAL64(num_dev_ino_buf, 4, st.st_dev);
SIVAL64(num_dev_ino_buf, 4+8, st.st_ino);
} else {
SIVAL64(num_dev_ino_buf, 4, 0);
SIVAL64(num_dev_ino_buf, 4+8, 0);
}
SIVAL(num_dev_ino_buf, 0, num);
send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
} else
send_msg_int(MSG_SUCCESS, num);
}
static void got_flist_entry_status(enum festatus status, int ndx)
{
struct file_list *flist = flist_for_ndx(ndx, "got_flist_entry_status");
if (ndx < flist->ndx_start)
exit_cleanup(RERR_PROTOCOL);
if (remove_source_files) {
active_filecnt--;
active_bytecnt -= F_LENGTH(flist->files[ndx - flist->ndx_start]);
}
if (inc_recurse)
flist->in_progress--;
switch (status) {
case FES_SUCCESS:
if (remove_source_files && write_batch >= 0) {
if (local_server)
send_msg(MSG_SUCCESS, num_dev_ino_buf, sizeof num_dev_ino_buf, -1);
else
send_msg_int(MSG_SUCCESS, ndx);
}
/* FALL THROUGH */
case FES_NO_SEND:
#ifdef SUPPORT_HARD_LINKS
if (preserve_hard_links) {
struct file_struct *file = flist->files[ndx - flist->ndx_start];
if (F_IS_HLINKED(file)) {
if (status == FES_NO_SEND)
flist_ndx_push(&hlink_list, -2); /* indicates a failure follows */
flist_ndx_push(&hlink_list, ndx);
if (inc_recurse)
flist->in_progress++;
}
}
#endif
break;
case FES_REDO:
if (read_batch) {
if (inc_recurse)
flist->in_progress++;
break;
}
if (inc_recurse)
flist->to_redo++;
flist_ndx_push(&redo_list, ndx);
break;
}
}
/* Note the fds used for the main socket (which might really be a pipe
* for a local transfer, but we can ignore that). */
void io_set_sock_fds(int f_in, int f_out)
{
sock_f_in = f_in;
sock_f_out = f_out;
}
void set_io_timeout(int secs)
{
/* A negative timeout is meaningless; treat it as "no timeout" rather than
* letting it drive allowed_lull / select_timeout negative (a tight loop).
* (--timeout is parsed by options.c as a plain int, so it can be negative.) */
if (secs < 0)
secs = 0;
io_timeout = secs;
/* Compute ceil(io_timeout/2) in a wider type: io_timeout can be INT_MAX
* (a peer's MSG_IO_TIMEOUT -- now capped in read_a_msg() -- or an operator
* --timeout, which options.c parses unbounded), and a plain "io_timeout + 1"
* would overflow to a negative allowed_lull / select_timeout. poll() now
* takes a millisecond count where negative means "wait forever", so this
* would hang the process rather than spin it -- and it still fires a
* keepalive flood. poll_timeout_ms() clamps as well; keep both. */
allowed_lull = (int)(((int64)io_timeout + 1) / 2);
/* The generator and sender derive an int loop-check limit as
* allowed_lull * 5; keep allowed_lull small enough that that product can't
* overflow either. The cap is invisible to real use -- allowed_lull is the
* keep-alive half-interval and INT_MAX/5 seconds is over 13 years. */
if (allowed_lull > INT_MAX / 5)
allowed_lull = INT_MAX / 5;
if (!io_timeout || allowed_lull > SELECT_TIMEOUT)
select_timeout = SELECT_TIMEOUT;
else
select_timeout = allowed_lull;
if (read_batch)
allowed_lull = 0;
}
void set_daemon_handshake_timeout(int secs)
{
if (secs > 0)
daemon_handshake_deadline = time(NULL) + secs;
else
daemon_handshake_deadline = 0;
}
static void check_for_d_option_error(const char *msg)
{
static const char rsync263_opts[] = "BCDHIKLPRSTWabceghlnopqrtuvxz";
const char *colon;
int saw_d = 0;
if (*msg != 'r'
|| strncmp(msg, REMOTE_OPTION_ERROR, sizeof REMOTE_OPTION_ERROR - 1) != 0)
return;
msg += sizeof REMOTE_OPTION_ERROR - 1;
if (*msg == '-' || (colon = strchr(msg, ':')) == NULL
|| strncmp(colon, REMOTE_OPTION_ERROR2, sizeof REMOTE_OPTION_ERROR2 - 1) != 0)
return;
for ( ; *msg != ':'; msg++) {
if (*msg == 'd')
saw_d = 1;
else if (*msg == 'e')
break;
else if (strchr(rsync263_opts, *msg) == NULL)
return;
}
if (saw_d) {
rprintf(FWARNING, "*** Try using \"--old-d\" if remote rsync is <= 2.6.3 ***\n");
}
}
/* This is used by the generator to limit how many file transfers can
* be active at once when --remove-source-files is specified. Without
* this, sender-side deletions were mostly happening at the end. */
void increment_active_files(int ndx, int itemizing, enum logcode code)
{
while (1) {
/* TODO: tune these limits? */
int limit = active_bytecnt >= 128*1024 ? 10 : 50;
if (active_filecnt < limit)
break;
check_for_finished_files(itemizing, code, 0);
if (active_filecnt < limit)
break;
wait_for_receiver();
}
active_filecnt++;
active_bytecnt += F_LENGTH(cur_flist->files[ndx - cur_flist->ndx_start]);
}
int get_redo_num(void)
{
return flist_ndx_pop(&redo_list);
}
int get_hlink_num(void)
{
return flist_ndx_pop(&hlink_list);
}
/* When we're the receiver and we have a local --files-from list of names
* that needs to be sent over the socket to the sender, we have to do two
* things at the same time: send the sender a list of what files we're
* processing and read the incoming file+info list from the sender. We do
* this by making recv_file_list() call forward_filesfrom_data(), which
* will ensure that we forward data to the sender until we get some data
* for recv_file_list() to use. */
void start_filesfrom_forwarding(int fd)
{
if (protocol_version < 31 && OUT_MULTIPLEXED) {
/* Older protocols send the files-from data w/o packaging
* it in multiplexed I/O packets, so temporarily switch
* to buffered I/O to match this behavior. */
iobuf.msg.pos = iobuf.msg.len = 0; /* Be extra sure no messages go out. */
ff_reenable_multiplex = io_end_multiplex_out(MPLX_TO_BUFFERED);
}
ff_forward_fd = fd;
alloc_xbuf(&ff_xb, FILESFROM_BUFLEN);
}
/* Read a line into the "buf" buffer. */
int read_line(int fd, char *buf, size_t bufsiz, int flags)
{
char ch, *s, *eob;
#ifdef ICONV_OPTION
if (flags & RL_CONVERT && iconv_buf.size < bufsiz)
realloc_xbuf(&iconv_buf, ROUND_UP_1024(bufsiz) + 1024);
#endif
start:
#ifdef ICONV_OPTION
s = flags & RL_CONVERT ? iconv_buf.buf : buf;
#else
s = buf;
#endif
eob = s + bufsiz - 1;
while (1) {
/* We avoid read_byte() for files because files can return an EOF. */
if (fd == iobuf.in_fd)
ch = read_byte(fd);
else if (safe_read(fd, &ch, 1) == 0)
break;
if (flags & RL_EOL_NULLS ? ch == '\0' : (ch == '\r' || ch == '\n')) {
/* Skip empty lines if dumping comments. */
if (flags & RL_DUMP_COMMENTS && s == buf)
continue;
break;
}
if (s < eob)
*s++ = ch;
}
*s = '\0';
if (flags & RL_DUMP_COMMENTS && (*buf == '#' || *buf == ';'))
goto start;
#ifdef ICONV_OPTION
if (flags & RL_CONVERT) {
xbuf outbuf;
INIT_XBUF(outbuf, buf, 0, bufsiz);
iconv_buf.pos = 0;
iconv_buf.len = s - iconv_buf.buf;
iconvbufs(ic_recv, &iconv_buf, &outbuf,
ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_INIT);
outbuf.buf[outbuf.len] = '\0';
return outbuf.len;
}
#endif
return s - buf;
}
/* Reverse safe_arg()'s backslash escaping of a daemon option arg, the way a
* remote shell un-escapes args for the ssh transport. In place; \X -> X. */
static void unbackslash_arg(char *s)
{
char *f = s, *t = s;
while (*f) {
if (*f == '\\' && f[1])
f++;
*t++ = *f++;
}
*t = '\0';
}
#define MAX_DAEMON_ARGS (MAX_ARGS * 16)
void read_args(int f_in, char *mod_name, char *buf, size_t bufsiz, int rl_nulls,
int unescape, char ***argv_p, int *argc_p, char **request_p)
{
int maxargs = MAX_ARGS;
int dot_pos = 0, argc = 0, request_len = 0;
char **argv, *p;
int rl_flags = (rl_nulls ? RL_EOL_NULLS : 0);
#ifdef ICONV_OPTION
rl_flags |= (protect_args && ic_recv != (iconv_t)-1 ? RL_CONVERT : 0);
#endif
argv = new_array(char *, maxargs);
if (mod_name && !protect_args)
argv[argc++] = "rsyncd";
if (request_p)
*request_p = NULL;
while (1) {
if (read_line(f_in, buf, bufsiz, rl_flags) == 0)
break;
if (mod_name && argc >= MAX_DAEMON_ARGS - 1) {
rprintf(FERROR, "too many daemon arguments\n");
exit_cleanup(RERR_PROTOCOL);
}
if (argc == maxargs-1) {
maxargs += MAX_ARGS;
argv = realloc_array(argv, char *, maxargs);
}
if (dot_pos) {
if (request_p && request_len < 1024) {
int len = strlen(buf);
if (request_len)
request_p[0][request_len++] = ' ';
*request_p = realloc_array(*request_p, char, request_len + len + 1);
memcpy(*request_p + request_len, buf, len + 1);
request_len += len;
}
if (mod_name)
glob_expand_module(mod_name, buf, &argv, &argc, &maxargs);
else
glob_expand(buf, &argv, &argc, &maxargs);
} else {
p = strdup(buf);
/* An option arg the client escaped with safe_arg() (no
* remote shell un-escapes it for a daemon). File args
* after the dot are handled by glob_expand() below. */
if (unescape)
unbackslash_arg(p);
argv[argc++] = p;
if (*p == '.' && p[1] == '\0')
dot_pos = argc;
}
}
/* glob_expand()/glob_match() reserve glob.argc+1 slots -- room for the
* entry being added but not for this trailing NULL. A post-dot line
* whose " mod/" splits land argc on exactly maxargs (or any later
* ENSURE_MEMSPACE doubling boundary) would otherwise make the next
* store an 8-byte NULL write one slot past the argv allocation. */
if (argc >= maxargs)
argv = realloc_array(argv, char *, maxargs = argc + 1);
argv[argc] = NULL;
glob_expand(NULL, NULL, NULL, NULL);
*argc_p = argc;
*argv_p = argv;
}
BOOL io_start_buffering_out(int f_out)
{
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_start_buffering_out(%d)\n", who_am_i(), f_out);
if (iobuf.out.buf) {
if (iobuf.out_fd == -1)
iobuf.out_fd = f_out;
else if (iobuf.out_fd >= 0)
assert(f_out == iobuf.out_fd);
/* else out_fd == -2: peer already gone; leave it dead. */
return False;
}
alloc_xbuf(&iobuf.out, ROUND_UP_1024(IO_BUFFER_SIZE * 2));
iobuf.out_fd = f_out;
return True;
}
BOOL io_start_buffering_in(int f_in)
{
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_start_buffering_in(%d)\n", who_am_i(), f_in);
if (iobuf.in.buf) {
if (iobuf.in_fd == -1)
iobuf.in_fd = f_in;
else if (iobuf.in_fd >= 0)
assert(f_in == iobuf.in_fd);
/* else in_fd == -2: peer already EOF'd; leave it dead. */
return False;
}
alloc_xbuf(&iobuf.in, ROUND_UP_1024(IO_BUFFER_SIZE));
iobuf.in_fd = f_in;
return True;
}
void io_end_buffering_in(BOOL free_buffers)
{
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] io_end_buffering_in(IOBUF_%s_BUFS)\n",
who_am_i(), free_buffers ? "FREE" : "KEEP");
}
if (free_buffers)
free_xbuf(&iobuf.in);
else
iobuf.in.pos = iobuf.in.len = 0;
iobuf.in_fd = -1;
}
void io_end_buffering_out(BOOL free_buffers)
{
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2)) {
rprintf(FINFO, "[%s] io_end_buffering_out(IOBUF_%s_BUFS)\n",
who_am_i(), free_buffers ? "FREE" : "KEEP");
}
io_flush(FULL_FLUSH);
if (free_buffers) {
free_xbuf(&iobuf.out);
free_xbuf(&iobuf.msg);
}
iobuf.out_fd = -1;
}
void maybe_flush_socket(int important)
{
if (flist_eof && iobuf.out.buf && iobuf.out.len > iobuf.out_empty_len
&& (important || time(NULL) - last_io_out >= 5))
io_flush(NORMAL_FLUSH);
}
/* Older rsync versions used to send either a MSG_NOOP (protocol 30) or a
* raw-data-based keep-alive (protocol 29), both of which implied forwarding of
* the message through the sender. Since the new timeout method does not need
* any forwarding, we just send an empty MSG_DATA message, which works with all
* rsync versions. This avoids any message forwarding, and leaves the raw-data
* stream alone (since we can never be quite sure if that stream is in the
* right state for a keep-alive message). */
void maybe_send_keepalive(time_t now, int flags)
{
if (flags & MSK_ACTIVE_RECEIVER)
last_io_in = now; /* Fudge things when we're working hard on the files. */
/* Early in the transfer (before the receiver forks) the receiving side doesn't
* care if it hasn't sent data in a while as long as it is receiving data (in
* fact, a pre-3.1.0 rsync would die if we tried to send it a keep alive during
* this time). So, if we're an early-receiving proc, just return and let the
* incoming data determine if we timeout. */
if (!am_sender && !am_receiver && !am_generator)
return;
if (now - last_io_out >= allowed_lull) {
/* The receiver is special: it only sends keep-alive messages if it is
* actively receiving data. Otherwise, it lets the generator timeout. */
if (am_receiver && now - last_io_in >= io_timeout)
return;
if (!iobuf.msg.len && iobuf.out.len == iobuf.out_empty_len)
send_msg(MSG_DATA, "", 0, 0);
if (!(flags & MSK_ALLOW_FLUSH)) {
/* Let the caller worry about writing out the data. */
} else if (iobuf.msg.len)
perform_io(iobuf.msg.size - iobuf.msg.len + 1, PIO_NEED_MSGROOM);
else if (iobuf.out.len > iobuf.out_empty_len)
io_flush(NORMAL_FLUSH);
}
}
void start_flist_forward(int ndx)
{
write_int(iobuf.out_fd, ndx);
forward_flist_data = 1;
}
void stop_flist_forward(void)
{
forward_flist_data = 0;
}
/* Read a message from a multiplexed source. */
static void read_a_msg(void)
{
char data[BIGPATHBUFLEN];
int tag, val;
size_t msg_bytes;
/* This ensures that perform_io() does not try to do any message reading
* until we've read all of the data for this message. We should also
* try to avoid calling things that will cause data to be written via
* perform_io() prior to this being reset to 1. */
iobuf.in_multiplexed = -1;
tag = raw_read_int();
msg_bytes = tag & 0xFFFFFF;
tag = (tag >> 24) - MPLEX_BASE;
if (msgs2stderr == 1 && DEBUG_GTE(IO, 1)) {
rprintf(FINFO, "[%s] got msg=%d, len=%" SIZE_T_FMT_MOD "d\n",
who_am_i(), (int)tag, (SIZE_T_FMT_CAST)msg_bytes);
}
switch (tag) {
case MSG_DATA:
assert(iobuf.raw_input_ends_before == 0);
/* Though this does not yet read the data, we do mark where in
* the buffer the msg data will end once it is read. It is
* possible that this points off the end of the buffer, in
* which case the gradual reading of the input stream will
* cause this value to wrap around and eventually become real. */
if (msg_bytes)
iobuf.raw_input_ends_before = iobuf.in.pos + msg_bytes;
iobuf.in_multiplexed = 1;
break;
case MSG_STATS:
if (msg_bytes != sizeof stats.total_read || !am_generator)
goto invalid_msg;
raw_read_buf((char*)&stats.total_read, sizeof stats.total_read);
iobuf.in_multiplexed = 1;
break;
case MSG_REDO:
if (msg_bytes != 4 || !am_generator)
goto invalid_msg;
val = raw_read_int();
iobuf.in_multiplexed = 1;
got_flist_entry_status(FES_REDO, val);
break;
case MSG_IO_ERROR:
if (msg_bytes != 4)
goto invalid_msg;
val = raw_read_int();
val &= IOERR_VALID_MASK;
io_error |= val;
if (am_receiver)
send_msg_int(MSG_IO_ERROR, val);
iobuf.in_multiplexed = 1;
break;
case MSG_IO_TIMEOUT:
if (msg_bytes != 4 || am_server || am_generator)
goto invalid_msg;
val = raw_read_int();
iobuf.in_multiplexed = 1;
/* The peer may only ask us to use a SHORTER timeout (a stricter cap); a
* non-positive value would disable our --timeout entirely, letting a
* malicious server hang the client indefinitely, so ignore it. A very
* large value (near INT_MAX) would overflow the (io_timeout + 1) / 2
* computation in set_io_timeout(), wrapping allowed_lull and
* select_timeout negative -- which poll() reads as "wait forever",
* hanging the client. Cap at 24 hours. */
if (val <= 0 || val > 86400)
break;
if (!io_timeout || io_timeout > val) {
if (INFO_GTE(MISC, 2))
rprintf(FINFO, "Setting --timeout=%d to match server\n", val);
set_io_timeout(val);
}
break;
case MSG_NOOP:
/* Support protocol-30 keep-alive method. */
if (msg_bytes != 0)
goto invalid_msg;
if (am_sender)
maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
iobuf.in_multiplexed = 1;
break;
case MSG_DELETED:
if (msg_bytes >= sizeof data)
goto overflow;
if (am_generator) {
raw_read_buf(data, msg_bytes);
send_msg(MSG_DELETED, data, msg_bytes, 1);
iobuf.in_multiplexed = 1;
break;
}
#ifdef ICONV_OPTION
if (ic_recv != (iconv_t)-1) {
xbuf outbuf, inbuf;
char ibuf[512];
int add_null = 0;
int flags = ICB_INCLUDE_BAD | ICB_INIT;
INIT_CONST_XBUF(outbuf, data);
INIT_XBUF(inbuf, ibuf, 0, (size_t)-1);
while (msg_bytes) {
size_t len = msg_bytes > sizeof ibuf - inbuf.len ? sizeof ibuf - inbuf.len : msg_bytes;
raw_read_buf(ibuf + inbuf.len, len);
inbuf.pos = 0;
inbuf.len += len;
if (!(msg_bytes -= len) && !ibuf[inbuf.len-1])
inbuf.len--, add_null = 1;
if (iconvbufs(ic_send, &inbuf, &outbuf, flags) < 0) {
if (errno == E2BIG)
goto overflow;
/* Buffer ended with an incomplete char, so move the
* bytes to the start of the buffer and continue. */
memmove(ibuf, ibuf + inbuf.pos, inbuf.len);
}
flags &= ~ICB_INIT;
}
if (add_null) {
if (outbuf.len == outbuf.size)
goto overflow;
outbuf.buf[outbuf.len++] = '\0';
}
msg_bytes = outbuf.len;
} else
#endif
raw_read_buf(data, msg_bytes);
/* A directory name was sent with the trailing null */
if (msg_bytes > 0 && !data[msg_bytes-1])
log_delete(data, S_IFDIR);
else {
data[msg_bytes] = '\0';
log_delete(data, S_IFREG);
}
iobuf.in_multiplexed = 1;
break;
case MSG_SUCCESS:
if (msg_bytes != (local_server ? 4+8+8 : 4)) {
invalid_msg:
rprintf(FERROR, "invalid multi-message %d:%lu [%s%s]\n",
tag, (unsigned long)msg_bytes, who_am_i(),
inc_recurse ? "/inc" : "");
exit_cleanup(RERR_STREAMIO);
}
raw_read_buf(num_dev_ino_buf, msg_bytes);
val = IVAL(num_dev_ino_buf, 0);
iobuf.in_multiplexed = 1;
if (am_generator)
got_flist_entry_status(FES_SUCCESS, val);
else
successful_send(val);
break;
case MSG_NO_SEND:
if (msg_bytes != 4)
goto invalid_msg;
val = raw_read_int();
if (am_generator)
got_flist_entry_status(FES_NO_SEND, val);
else
send_msg_int(MSG_NO_SEND, val);
iobuf.in_multiplexed = 1;
break;
case MSG_ERROR_SOCKET:
case MSG_ERROR_UTF8:
case MSG_CLIENT:
case MSG_LOG:
if (!am_generator)
goto invalid_msg;
if (tag == MSG_ERROR_SOCKET)
msgs2stderr = 1;
/* FALL THROUGH */
case MSG_INFO:
case MSG_ERROR:
case MSG_ERROR_XFER:
case MSG_WARNING:
if (msg_bytes >= sizeof data) {
overflow:
rprintf(FERROR,
"multiplexing overflow %d:%lu [%s%s]\n",
tag, (unsigned long)msg_bytes, who_am_i(),
inc_recurse ? "/inc" : "");
exit_cleanup(RERR_STREAMIO);
}
raw_read_buf(data, msg_bytes);
/* We don't set in_multiplexed value back to 1 before writing this message
* because the write might loop back and read yet another message, over and
* over again, while waiting for room to put the message in the msg buffer. */
rwrite((enum logcode)tag, data, msg_bytes, !am_generator);
iobuf.in_multiplexed = 1;
if (first_message) {
if (list_only && !am_sender && tag == 1 && msg_bytes < sizeof data) {
data[msg_bytes] = '\0';
check_for_d_option_error(data);
}
first_message = 0;
}
break;
case MSG_ERROR_EXIT:
if (msg_bytes == 4)
val = raw_read_int();
else if (msg_bytes == 0)
val = 0;
else
goto invalid_msg;
iobuf.in_multiplexed = 1;
if (DEBUG_GTE(EXIT, 3)) {
rprintf(FINFO, "[%s] got MSG_ERROR_EXIT with %" SIZE_T_FMT_MOD "d bytes\n",
who_am_i(), (SIZE_T_FMT_CAST)msg_bytes);
}
if (msg_bytes == 0) {
if (!am_sender && !am_generator) {
if (DEBUG_GTE(EXIT, 3)) {
rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT (len 0)\n",
who_am_i());
}
send_msg(MSG_ERROR_EXIT, "", 0, 0);
io_flush(FULL_FLUSH);
}
} else if (protocol_version >= 31) {
if (am_generator || am_receiver) {
if (DEBUG_GTE(EXIT, 3)) {
rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT with exit_code %d\n",
who_am_i(), val);
}
send_msg_int(MSG_ERROR_EXIT, val);
} else {
if (DEBUG_GTE(EXIT, 3)) {
rprintf(FINFO, "[%s] sending MSG_ERROR_EXIT (len 0)\n",
who_am_i());
}
send_msg(MSG_ERROR_EXIT, "", 0, 0);
}
}
/* Send a negative linenum so that we don't end up
* with a duplicate exit message. */
_exit_cleanup(val, __FILE__, 0 - __LINE__);
default:
rprintf(FERROR, "unexpected tag %d [%s%s]\n",
tag, who_am_i(), inc_recurse ? "/inc" : "");
exit_cleanup(RERR_STREAMIO);
}
assert(iobuf.in_multiplexed > 0);
}
static void drain_multiplex_messages(void)
{
while (IN_MULTIPLEXED_AND_READY && iobuf.in.len) {
if (iobuf.raw_input_ends_before) {
size_t raw_len = iobuf.raw_input_ends_before - iobuf.in.pos;
iobuf.raw_input_ends_before = 0;
if (raw_len >= iobuf.in.len) {
iobuf.in.len = 0;
break;
}
iobuf.in.len -= raw_len;
if ((iobuf.in.pos += raw_len) >= iobuf.in.size)
iobuf.in.pos -= iobuf.in.size;
}
read_a_msg();
}
}
void wait_for_receiver(void)
{
if (!iobuf.raw_input_ends_before)
read_a_msg();
if (iobuf.raw_input_ends_before) {
int ndx = read_int(iobuf.in_fd);
if (ndx < 0) {
switch (ndx) {
case NDX_FLIST_EOF:
flist_eof = 1;
if (DEBUG_GTE(FLIST, 3))
rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
break;
case NDX_DONE:
msgdone_cnt++;
break;
default:
exit_cleanup(RERR_STREAMIO);
}
} else {
struct file_list *flist;
flist_receiving_enabled = False;
if (DEBUG_GTE(FLIST, 2)) {
rprintf(FINFO, "[%s] receiving flist for dir %d\n",
who_am_i(), ndx);
}
flist = recv_file_list(iobuf.in_fd, ndx);
flist->parent_ndx = ndx;
#ifdef SUPPORT_HARD_LINKS
if (preserve_hard_links)
match_hard_links(flist);
#endif
flist_receiving_enabled = True;
}
}
}
unsigned short read_shortint(int f)
{
char b[2];
read_buf(f, b, 2);
return (UVAL(b, 1) << 8) + UVAL(b, 0);
}
int32 read_int(int f)
{
char b[4];
int32 num;
read_buf(f, b, 4);
num = IVAL(b, 0);
#if SIZEOF_INT32 > 4
if (num & (int32)0x80000000)
num |= ~(int32)0xffffffff;
#endif
return num;
}
uint32 read_uint(int f)
{
char b[4];
read_buf(f, b, 4);
return IVAL(b, 0);
}
int32 read_varint(int f)
{
union {
char b[5];
int32 x;
} u;
uchar ch;
int extra;
u.x = 0;
ch = read_byte(f);
extra = int_byte_extra[ch / 4];
if (extra) {
uchar bit = ((uchar)1<<(8-extra));
if (extra >= (int)sizeof u.b) {
rprintf(FERROR, "Overflow in read_varint()\n");
exit_cleanup(RERR_STREAMIO);
}
read_buf(f, u.b, extra);
u.b[extra] = ch & (bit-1);
} else
u.b[0] = ch;
#if CAREFUL_ALIGNMENT
u.x = IVAL(u.b,0);
#endif
#if SIZEOF_INT32 > 4
if (u.x & (int32)0x80000000)
u.x |= ~(int32)0xffffffff;
#endif
return u.x;
}
int64 read_varlong(int f, uchar min_bytes)
{
union {
char b[9];
int64 x;
} u;
char b2[8];
int extra;
#if SIZEOF_INT64 < 8
memset(u.b, 0, 8);
#else
u.x = 0;
#endif
read_buf(f, b2, min_bytes);
memcpy(u.b, b2+1, min_bytes-1);
extra = int_byte_extra[CVAL(b2, 0) / 4];
if (extra) {
uchar bit = ((uchar)1<<(8-extra));
if (min_bytes + extra > (int)sizeof u.b) {
rprintf(FERROR, "Overflow in read_varlong()\n");
exit_cleanup(RERR_STREAMIO);
}
read_buf(f, u.b + min_bytes - 1, extra);
u.b[min_bytes + extra - 1] = CVAL(b2, 0) & (bit-1);
#if SIZEOF_INT64 < 8
if (min_bytes + extra > 5 || u.b[4] || CVAL(u.b,3) & 0x80) {
rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
exit_cleanup(RERR_UNSUPPORTED);
}
#endif
} else
u.b[min_bytes + extra - 1] = CVAL(b2, 0);
#if SIZEOF_INT64 < 8
u.x = IVAL(u.b,0);
#elif CAREFUL_ALIGNMENT
u.x = IVAL64(u.b,0);
#endif
return u.x;
}
/* Read an int32 and verify lo <= v <= hi. On out-of-range, abort with a
* protocol error naming "what". The bound is co-located with the read so it
* cannot be forgotten by a downstream user. */
int32 read_int_bounded(int f, int32 lo, int32 hi, const char *what)
{
int32 v = read_int(f);
if (v < lo || v > hi) {
rprintf(FERROR, "wire value %s out of range: %ld not in [%ld,%ld] [%s]\n",
what, (long)v, (long)lo, (long)hi, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
return v;
}
/* As read_int_bounded but for varint-encoded values. */
int32 read_varint_bounded(int f, int32 lo, int32 hi, const char *what)
{
int32 v = read_varint(f);
if (v < lo || v > hi) {
rprintf(FERROR, "wire value %s out of range: %ld not in [%ld,%ld] [%s]\n",
what, (long)v, (long)lo, (long)hi, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
return v;
}
/* Read a varint that will be used as a size_t. Rejects negative values
* (which would wrap to ~SIZE_MAX) and values exceeding the supplied max. */
size_t read_varint_size(int f, size_t max, const char *what)
{
int32 v = read_varint(f);
if (v < 0 || (size_t)v > max) {
rprintf(FERROR, "wire size %s out of range: %ld > %lu [%s]\n",
what, (long)v, (unsigned long)max, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
return (size_t)v;
}
int64 read_longint(int f)
{
#if SIZEOF_INT64 >= 8
char b[9];
#endif
int32 num = read_int(f);
if (num != (int32)0xffffffff)
return num;
#if SIZEOF_INT64 < 8
rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
exit_cleanup(RERR_UNSUPPORTED);
#else
read_buf(f, b, 8);
return IVAL(b,0) | (((int64)IVAL(b,4))<<32);
#endif
}
/* Debugging note: this will be named read_buf_() when using an external zlib. */
void read_buf(int f, char *buf, size_t len)
{
if (f != iobuf.in_fd) {
if (safe_read(f, buf, len) != len)
whine_about_eof(False); /* Doesn't return. */
goto batch_copy;
}
if (!IN_MULTIPLEXED) {
raw_read_buf(buf, len);
total_data_read += len;
if (forward_flist_data)
write_buf(iobuf.out_fd, buf, len);
batch_copy:
if (f == write_batch_monitor_in)
safe_write(batch_fd, buf, len);
return;
}
while (1) {
size_t siz;
while (!iobuf.raw_input_ends_before)
read_a_msg();
siz = MIN(len, iobuf.raw_input_ends_before - iobuf.in.pos);
if (siz >= iobuf.in.size)
siz = iobuf.in.size;
raw_read_buf(buf, siz);
total_data_read += siz;
if (forward_flist_data)
write_buf(iobuf.out_fd, buf, siz);
if (f == write_batch_monitor_in)
safe_write(batch_fd, buf, siz);
if ((len -= siz) == 0)
break;
buf += siz;
}
}
void read_sbuf(int f, char *buf, size_t len)
{
read_buf(f, buf, len);
buf[len] = '\0';
}
uchar read_byte(int f)
{
uchar c;
read_buf(f, (char*)&c, 1);
return c;
}
int read_vstring(int f, char *buf, int bufsize)
{
int len = read_byte(f);
if (len & 0x80)
len = (len & ~0x80) * 0x100 + read_byte(f);
if (len >= bufsize) {
rprintf(FERROR, "over-long vstring received (%d > %d)\n",
len, bufsize - 1);
return -1;
}
if (len)
read_buf(f, buf, len);
buf[len] = '\0';
return len;
}
/* Populate a sum_struct with values from the socket. This is
* called by both the sender and the receiver. */
void read_sum_head(int f, struct sum_struct *sum)
{
int32 max_blength = protocol_version < 30 ? OLD_MAX_BLOCK_SIZE : MAX_BLOCK_SIZE;
sum->count = read_int(f);
if (sum->count < 0) {
rprintf(FERROR, "Invalid checksum count %ld [%s]\n",
(long)sum->count, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
/* Guard against integer overflow in downstream allocations sized by
* count*element_size. my_alloc uses divide-not-multiply so it is
* already wraparound-safe, but checking here gives a clearer error
* and also covers the (size_t)count * xfer_sum_len arithmetic that
* is performed *before* reaching my_alloc. */
if (xfer_sum_len > 0 && (size_t)sum->count > SIZE_MAX / (size_t)xfer_sum_len) {
rprintf(FERROR, "Invalid checksum count %ld (too large) [%s]\n",
(long)sum->count, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
if ((size_t)sum->count > SIZE_MAX / sizeof(struct sum_buf)) {
rprintf(FERROR, "Invalid checksum count %ld (sum_buf overflow) [%s]\n",
(long)sum->count, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
sum->blength = read_int(f);
if (sum->blength < 0 || sum->blength > max_blength) {
rprintf(FERROR, "Invalid block length %ld [%s]\n",
(long)sum->blength, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
if (sum->count && sum->blength == 0) {
rprintf(FERROR, "Invalid zero block length [%s]\n",
who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
#if SIZEOF_CAPITAL_OFF_T < 8
/* The append-mode callers compute (OFF_T)count * blength; on a 32-bit
* OFF_T that product can wrap even though both factors are individually
* in range, corrupting the lseek/loop bounds. Reject it early. */
if (sum->blength > 0 && sum->count > MAX_INT32 / sum->blength) {
rprintf(FERROR, "checksum count*blength overflows OFF_T [%s]\n",
who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
#endif
sum->s2length = protocol_version < 27 ? csum_length : (int)read_int(f);
if (sum->s2length < 0 || sum->s2length > xfer_sum_len) {
rprintf(FERROR, "Invalid checksum length %d [%s]\n",
sum->s2length, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
sum->remainder = read_int(f);
if (sum->remainder < 0 || sum->remainder > sum->blength) {
rprintf(FERROR, "Invalid remainder length %ld [%s]\n",
(long)sum->remainder, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
}
/* Send the values from a sum_struct over the socket. Set sum to
* NULL if there are no checksums to send. This is called by both
* the generator and the sender. */
void write_sum_head(int f, struct sum_struct *sum)
{
static struct sum_struct null_sum;
if (sum == NULL)
sum = &null_sum;
write_int(f, sum->count);
write_int(f, sum->blength);
if (protocol_version >= 27)
write_int(f, sum->s2length);
write_int(f, sum->remainder);
}
/* Sleep after writing to limit I/O bandwidth usage.
*
* @todo Rather than sleeping after each write, it might be better to
* use some kind of averaging. The current algorithm seems to always
* use a bit less bandwidth than specified, because it doesn't make up
* for slow periods. But arguably this is a feature. In addition, we
* ought to take the time used to write the data into account.
*
* During some phases of big transfers (file FOO is uptodate) this is
* called with a small bytes_written every time. As the kernel has to
* round small waits up to guarantee that we actually wait at least the
* requested number of microseconds, this can become grossly inaccurate.
* We therefore keep track of the bytes we've written over time and only
* sleep when the accumulated delay is at least 1 tenth of a second. */
static void sleep_for_bwlimit(int bytes_written)
{
static struct timeval prior_tv;
static long total_written = 0;
struct timeval tv, start_tv;
long elapsed_usec, sleep_usec;
#define ONE_SEC 1000000L /* # of microseconds in a second */
total_written += bytes_written;
gettimeofday(&start_tv, NULL);
if (prior_tv.tv_sec) {
elapsed_usec = (start_tv.tv_sec - prior_tv.tv_sec) * ONE_SEC
+ (start_tv.tv_usec - prior_tv.tv_usec);
total_written -= (int64)elapsed_usec * bwlimit / (ONE_SEC/1024);
if (total_written < 0)
total_written = 0;
}
sleep_usec = total_written * (ONE_SEC/1024) / bwlimit;
if (sleep_usec < ONE_SEC / 10) {
prior_tv = start_tv;
return;
}
tv.tv_sec = sleep_usec / ONE_SEC;
tv.tv_usec = sleep_usec % ONE_SEC;
select(0, NULL, NULL, NULL, &tv);
gettimeofday(&prior_tv, NULL);
elapsed_usec = (prior_tv.tv_sec - start_tv.tv_sec) * ONE_SEC
+ (prior_tv.tv_usec - start_tv.tv_usec);
total_written = (sleep_usec - elapsed_usec) * bwlimit / (ONE_SEC/1024);
}
void io_flush(int flush_type)
{
if (iobuf.out.len > iobuf.out_empty_len) {
if (flush_type == FULL_FLUSH) /* flush everything in the output buffers */
perform_io(iobuf.out.size - iobuf.out_empty_len, PIO_NEED_OUTROOM);
else if (flush_type == NORMAL_FLUSH) /* flush at least 1 byte */
perform_io(iobuf.out.size - iobuf.out.len + 1, PIO_NEED_OUTROOM);
/* MSG_FLUSH: flush iobuf.msg only */
}
if (iobuf.msg.len)
perform_io(iobuf.msg.size, PIO_NEED_MSGROOM);
}
void write_shortint(int f, unsigned short x)
{
char b[2];
b[0] = (char)x;
b[1] = (char)(x >> 8);
write_buf(f, b, 2);
}
void write_int(int f, int32 x)
{
char b[4];
SIVAL(b, 0, x);
write_buf(f, b, 4);
}
void write_varint(int f, int32 x)
{
char b[5] = {0}; /* {0} only to satisfy the analyzer: it doesn't model SIVAL initialising b[1..4] */
uchar bit;
int cnt;
SIVAL(b, 1, x);
for (cnt = 4; cnt > 1 && b[cnt] == 0; cnt--) {}
bit = ((uchar)1<<(7-cnt+1));
if (CVAL(b, cnt) >= bit) {
cnt++;
*b = ~(bit-1);
} else if (cnt > 1)
*b = b[cnt] | ~(bit*2-1);
else
*b = b[1];
write_buf(f, b, cnt);
}
void write_varlong(int f, int64 x, uchar min_bytes)
{
char b[9] = {0}; /* {0} only to satisfy the analyzer: it doesn't model SIVAL64 initialising b[1..8] */
uchar bit;
int cnt = 8;
#if SIZEOF_INT64 >= 8
SIVAL64(b, 1, x);
#else
SIVAL(b, 1, x);
if (x <= 0x7FFFFFFF && x >= 0)
memset(b + 5, 0, 4);
else {
rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
exit_cleanup(RERR_UNSUPPORTED);
}
#endif
while (cnt > min_bytes && b[cnt] == 0)
cnt--;
bit = ((uchar)1<<(7-cnt+min_bytes));
if (CVAL(b, cnt) >= bit) {
cnt++;
*b = ~(bit-1);
} else if (cnt > min_bytes)
*b = b[cnt] | ~(bit*2-1);
else
*b = b[cnt];
write_buf(f, b, cnt);
}
/*
* Note: int64 may actually be a 32-bit type if ./configure couldn't find any
* 64-bit types on this platform.
*/
void write_longint(int f, int64 x)
{
char b[12], * const s = b+4;
SIVAL(s, 0, x);
if (x <= 0x7FFFFFFF && x >= 0) {
write_buf(f, s, 4);
return;
}
#if SIZEOF_INT64 < 8
rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
exit_cleanup(RERR_UNSUPPORTED);
#else
memset(b, 0xFF, 4);
SIVAL(s, 4, x >> 32);
write_buf(f, b, 12);
#endif
}
void write_bigbuf(int f, const char *buf, size_t len)
{
size_t half_max = (iobuf.out.size - iobuf.out_empty_len) / 2;
while (len > half_max + 1024) {
write_buf(f, buf, half_max);
buf += half_max;
len -= half_max;
}
write_buf(f, buf, len);
}
void write_buf(int f, const char *buf, size_t len)
{
size_t pos, siz;
if (f != iobuf.out_fd) {
safe_write(f, buf, len);
goto batch_copy;
}
if (iobuf.out.len + len > iobuf.out.size)
perform_io(len, PIO_NEED_OUTROOM);
pos = iobuf.out.pos + iobuf.out.len; /* Must be set after any flushing. */
if (pos >= iobuf.out.size)
pos -= iobuf.out.size;
/* Handle a split copy if we wrap around the end of the circular buffer. */
if (pos >= iobuf.out.pos && (siz = iobuf.out.size - pos) < len) {
memcpy(iobuf.out.buf + pos, buf, siz);
memcpy(iobuf.out.buf, buf + siz, len - siz);
} else
memcpy(iobuf.out.buf + pos, buf, len);
iobuf.out.len += len;
total_data_written += len;
batch_copy:
if (f == write_batch_monitor_out)
safe_write(batch_fd, buf, len);
}
/* Write a string to the connection */
void write_sbuf(int f, const char *buf)
{
write_buf(f, buf, strlen(buf));
}
void write_byte(int f, uchar c)
{
write_buf(f, (char *)&c, 1);
}
void write_vstring(int f, const char *str, int len)
{
uchar lenbuf[3], *lb = lenbuf;
if (len > 0x7F) {
if (len > 0x7FFF) {
rprintf(FERROR,
"attempting to send over-long vstring (%d > %d)\n",
len, 0x7FFF);
exit_cleanup(RERR_PROTOCOL);
}
*lb++ = len / 0x100 + 0x80;
}
*lb = len;
write_buf(f, (char*)lenbuf, lb - lenbuf + 1);
if (len)
write_buf(f, str, len);
}
/* Send a file-list index using a byte-reduction method. */
void write_ndx(int f, int32 ndx)
{
static int32 prev_positive = -1, prev_negative = 1;
int32 diff, cnt = 0;
char b[6];
if (protocol_version < 30 || read_batch) {
write_int(f, ndx);
return;
}
/* Send NDX_DONE as a single-byte 0 with no side effects. Send
* negative nums as a positive after sending a leading 0xFF. */
if (ndx >= 0) {
diff = ndx - prev_positive;
prev_positive = ndx;
} else if (ndx == NDX_DONE) {
*b = 0;
write_buf(f, b, 1);
return;
} else {
b[cnt++] = (char)0xFF;
ndx = -ndx;
diff = ndx - prev_negative;
prev_negative = ndx;
}
/* A diff of 1 - 253 is sent as a one-byte diff; a diff of 254 - 32767
* or 0 is sent as a 0xFE + a two-byte diff; otherwise we send 0xFE
* & all 4 bytes of the (non-negative) num with the high-bit set. */
if (diff < 0xFE && diff > 0)
b[cnt++] = (char)diff;
else if (diff < 0 || diff > 0x7FFF) {
b[cnt++] = (char)0xFE;
b[cnt++] = (char)((ndx >> 24) | 0x80);
b[cnt++] = (char)ndx;
b[cnt++] = (char)(ndx >> 8);
b[cnt++] = (char)(ndx >> 16);
} else {
b[cnt++] = (char)0xFE;
b[cnt++] = (char)(diff >> 8);
b[cnt++] = (char)diff;
}
write_buf(f, b, cnt);
}
/* Receive a file-list index using a byte-reduction method. */
int32 read_ndx(int f)
{
static int32 prev_positive = -1, prev_negative = 1;
int32 *prev_ptr, num;
uint32 unum;
char b[4];
if (protocol_version < 30)
return read_int(f);
read_buf(f, b, 1);
if (CVAL(b, 0) == 0xFF) {
read_buf(f, b, 1);
prev_ptr = &prev_negative;
} else if (CVAL(b, 0) == 0)
return NDX_DONE;
else
prev_ptr = &prev_positive;
if (CVAL(b, 0) == 0xFE) {
read_buf(f, b, 2);
if (CVAL(b, 0) & 0x80) {
b[3] = CVAL(b, 0) & ~0x80;
b[0] = b[1];
read_buf(f, b+1, 2);
unum = IVAL(b, 0);
} else
unum = (UVAL(b,0)<<8) + UVAL(b,1) + (uint32)*prev_ptr;
} else
unum = UVAL(b, 0) + (uint32)*prev_ptr;
/* A peer-supplied index that overflows a signed int32 (used unchecked as a
* file-list index) is a protocol violation -- reject it here rather than
* relying on every downstream consumer to bounds-check. */
if (unum > (uint32)MAX_INT32) {
rprintf(FERROR, "Invalid file index: %lu [%s]\n",
(unsigned long)unum, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
num = (int32)unum;
*prev_ptr = num;
if (prev_ptr == &prev_negative)
num = -num;
return num;
}
/* Read a line of up to bufsiz-1 characters into buf. Strips
* the (required) trailing newline and all carriage returns.
* Returns 1 for success; 0 for I/O error or truncation. */
int read_line_old(int fd, char *buf, size_t bufsiz, int eof_ok)
{
assert(fd != iobuf.in_fd);
bufsiz--; /* leave room for the null */
while (bufsiz > 0) {
if (safe_read(fd, buf, 1) == 0) {
if (eof_ok)
break;
return 0;
}
if (*buf == '\0')
return 0;
if (*buf == '\n')
break;
if (*buf != '\r') {
buf++;
bufsiz--;
}
}
*buf = '\0';
return bufsiz > 0;
}
void io_printf(int fd, const char *format, ...)
{
va_list ap;
char buf[BIGPATHBUFLEN];
int len;
va_start(ap, format);
len = vsnprintf(buf, sizeof buf, format, ap);
va_end(ap);
if (len < 0)
exit_cleanup(RERR_PROTOCOL);
if (len >= (int)sizeof buf) {
rprintf(FERROR, "io_printf() was too long for the buffer.\n");
exit_cleanup(RERR_PROTOCOL);
}
write_sbuf(fd, buf);
}
/* Setup for multiplexing a MSG_* stream with the data stream. */
void io_start_multiplex_out(int fd)
{
io_flush(FULL_FLUSH);
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_start_multiplex_out(%d)\n", who_am_i(), fd);
if (!iobuf.msg.buf)
alloc_xbuf(&iobuf.msg, ROUND_UP_1024(IO_BUFFER_SIZE));
iobuf.out_empty_len = 4; /* See also OUT_MULTIPLEXED */
io_start_buffering_out(fd);
got_kill_signal = 0;
iobuf.raw_data_header_pos = iobuf.out.pos + iobuf.out.len;
iobuf.out.len += 4;
}
/* Setup for multiplexing a MSG_* stream with the data stream. */
void io_start_multiplex_in(int fd)
{
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_start_multiplex_in(%d)\n", who_am_i(), fd);
iobuf.in_multiplexed = 1; /* See also IN_MULTIPLEXED */
io_start_buffering_in(fd);
}
int io_end_multiplex_in(int mode)
{
int ret = iobuf.in_multiplexed ? iobuf.in_fd : -1;
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_end_multiplex_in(mode=%d)\n", who_am_i(), mode);
iobuf.in_multiplexed = 0;
if (mode == MPLX_SWITCHING)
iobuf.raw_input_ends_before = 0;
else
assert(iobuf.raw_input_ends_before == 0);
if (mode != MPLX_TO_BUFFERED)
io_end_buffering_in(mode);
return ret;
}
int io_end_multiplex_out(int mode)
{
int ret = iobuf.out_empty_len ? iobuf.out_fd : -1;
if (msgs2stderr == 1 && DEBUG_GTE(IO, 2))
rprintf(FINFO, "[%s] io_end_multiplex_out(mode=%d)\n", who_am_i(), mode);
if (mode != MPLX_TO_BUFFERED)
io_end_buffering_out(mode);
else
io_flush(FULL_FLUSH);
iobuf.out.len = 0;
iobuf.out_empty_len = 0;
if (got_kill_signal > 0) /* Just in case... */
handle_kill_signal(False);
got_kill_signal = -1;
return ret;
}
void start_write_batch(int fd)
{
/* Some communication has already taken place, but we don't
* enable batch writing until here so that we can write a
* canonical record of the communication even though the
* actual communication so far depends on whether a daemon
* is involved. */
write_int(batch_fd, protocol_version);
if (protocol_version >= 30)
write_varint(batch_fd, compat_flags);
write_int(batch_fd, checksum_seed);
if (am_sender)
write_batch_monitor_out = fd;
else
write_batch_monitor_in = fd;
}
void stop_write_batch(void)
{
write_batch_monitor_out = -1;
write_batch_monitor_in = -1;
}