+ realloc_xbuf(out, out->size + ROUND_UP_1024(in->len * 2));
+ }
+
+ if (flags & ICB_INIT)
+ iconv(ic, NULL, 0, NULL, 0);
+
+ ibuf = in->buf + in->pos;
+ icnt = in->len;
+
+ opos = out->pos + out->len;
+ if (flags & ICB_CIRCULAR_OUT) {
+ if (opos >= out->size) {
+ opos -= out->size;
+ /* We know that out->pos is not 0 due to the "no room" check
+ * above, so this can't go "negative". */
+ ocnt = out->pos - opos - 1;
+ } else {
+ /* Allow the use of all bytes to the physical end of the buffer
+ * unless pos is 0, in which case we reserve our trailing '\0'. */
+ ocnt = out->size - opos - (out->pos ? 0 : 1);
+ }
+ } else
+ ocnt = out->size - opos - 1;
+ obuf = out->buf + opos;
+
+ while (icnt) {
+ while (iconv(ic, &ibuf, &icnt, &obuf, &ocnt) == (size_t)-1) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EINVAL) {
+ if (!(flags & ICB_INCLUDE_INCOMPLETE))
+ goto finish;
+ } else if (errno == EILSEQ) {
+ if (!(flags & ICB_INCLUDE_BAD))
+ goto finish;
+ } else if (errno == E2BIG) {
+ size_t siz;
+ opos = obuf - out->buf;
+ if (flags & ICB_CIRCULAR_OUT && out->pos > 1 && opos > out->pos) {
+ /* We are in a divided circular buffer at the physical
+ * end with room to wrap to the start. If iconv() refused
+ * to use one or more trailing bytes in the buffer, we
+ * set the size to ignore the unused bytes. */
+ if (opos < out->size)
+ reduce_iobuf_size(out, opos);
+ obuf = out->buf;
+ ocnt = out->pos - 1;
+ continue;
+ }
+ if (!(flags & ICB_EXPAND_OUT) || flags & ICB_CIRCULAR_OUT) {
+ errno = E2BIG;
+ goto finish;
+ }
+ siz = ROUND_UP_1024(in->len * 2);
+ realloc_xbuf(out, out->size + siz);
+ obuf = out->buf + opos;
+ ocnt += siz;
+ continue;
+ } else {
+ rsyserr(FERROR, errno, "unexpected error from iconv()");
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+ *obuf++ = *ibuf++;
+ ocnt--, icnt--;
+ }
+ }
+
+ errno = 0;
+
+ finish:
+ opos = obuf - out->buf;
+ if (flags & ICB_CIRCULAR_OUT && opos < out->pos)
+ opos += out->size;
+ out->len = opos - out->pos;
+
+ in->len = icnt;
+ in->pos = ibuf - in->buf;
+
+ return errno ? -1 : 0;
+}
+#endif
+
+void send_protected_args(int fd, char *args[])
+{
+ int i;
+#ifdef ICONV_OPTION
+ int convert = ic_send != (iconv_t)-1;
+ xbuf outbuf, inbuf;
+
+ if (convert)
+ alloc_xbuf(&outbuf, 1024);
+#endif
+
+ for (i = 0; args[i]; i++) {} /* find first NULL */
+ args[i] = "rsync"; /* set a new arg0 */
+ if (DEBUG_GTE(CMD, 1))
+ print_child_argv("protected args:", args + i + 1);
+ do {
+ if (!args[i][0])
+ write_buf(fd, ".", 2);
+#ifdef ICONV_OPTION
+ else if (convert) {
+ INIT_XBUF_STRLEN(inbuf, args[i]);
+ iconvbufs(ic_send, &inbuf, &outbuf,
+ ICB_EXPAND_OUT | ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_INIT);
+ outbuf.buf[outbuf.len] = '\0';
+ write_buf(fd, outbuf.buf, outbuf.len + 1);
+ outbuf.len = 0;
+ }
+#endif
+ else
+ write_buf(fd, args[i], strlen(args[i]) + 1);
+ } while (args[++i]);
+ write_byte(fd, 0);
+
+#ifdef ICONV_OPTION
+ if (convert)
+ free(outbuf.buf);
+#endif
+}
+
+int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr, uchar *type_ptr,
+ char *buf, int *len_ptr)
+{
+ int len, iflags = 0;
+ struct file_list *flist;
+ uchar fnamecmp_type = FNAMECMP_FNAME;
+ int ndx;
+
+ read_loop:
+ while (1) {
+ ndx = read_ndx(f_in);
+
+ if (ndx >= 0)
+ break;
+ if (ndx == NDX_DONE)
+ return ndx;
+ if (ndx == NDX_DEL_STATS) {
+ read_del_stats(f_in);
+ if (am_sender && am_server)
+ write_del_stats(f_out);
+ continue;
+ }
+ if (!inc_recurse || am_sender) {
+ int last;
+ if (first_flist)
+ last = first_flist->prev->ndx_start + first_flist->prev->used - 1;
+ else
+ last = -1;
+ rprintf(FERROR,
+ "Invalid file index: %d (%d - %d) [%s]\n",
+ ndx, NDX_DONE, last, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ if (ndx == NDX_FLIST_EOF) {
+ flist_eof = 1;
+ if (DEBUG_GTE(FLIST, 3))
+ rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
+ write_int(f_out, NDX_FLIST_EOF);
+ continue;
+ }
+ ndx = NDX_FLIST_OFFSET - ndx;
+ if (ndx < 0 || ndx >= dir_flist->used) {
+ ndx = NDX_FLIST_OFFSET - ndx;
+ rprintf(FERROR,
+ "Invalid dir index: %d (%d - %d) [%s]\n",
+ ndx, NDX_FLIST_OFFSET,
+ NDX_FLIST_OFFSET - dir_flist->used + 1,
+ who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ if (DEBUG_GTE(FLIST, 2)) {
+ rprintf(FINFO, "[%s] receiving flist for dir %d\n",
+ who_am_i(), ndx);
+ }
+ /* Send all the data we read for this flist to the generator. */
+ start_flist_forward(ndx);
+ flist = recv_file_list(f_in);
+ flist->parent_ndx = ndx;
+ stop_flist_forward();
+ }
+
+ iflags = protocol_version >= 29 ? read_shortint(f_in)
+ : ITEM_TRANSFER | ITEM_MISSING_DATA;
+
+ /* Support the protocol-29 keep-alive style. */
+ if (protocol_version < 30 && ndx == cur_flist->used && iflags == ITEM_IS_NEW) {
+ if (am_sender)
+ maybe_send_keepalive(time(NULL), True);
+ goto read_loop;