void ClientHandler::loop_memoria() {
	bool tengo_el_token = false;
	do {
		try {
			tengo_el_token = false;
			grupo->lock_token();
			tengo_el_token = true;

		} catch (InterruptedSyscall & interruption) {
			Log::alert(interruption.what());
			tengo_el_token = false;
			leave = true;
		}
		try {
			send_token();

			if (recv_token() == 0) {
				leave = true;
			}

		} catch (OSError & error) {
#if DEBUG_CLIENT_HANDLER==1
			std::cout << nombre_cliente << "(" << nombre_grupo << ") Saliendo" << std::endl;
#endif
			Log::alert(error.what());
			leave = true;
		}

		try {
			if (leave) {
				leave_group();
			}
			if (tengo_el_token) {
				grupo->release_token(&cola_token_manager);
			}
			tengo_el_token = false;
		} catch (OSError & error) {
		}
	} while (!leave);
	try {
		mensaje.respuesta = mensajes::LEAVE_OK;
		socket.sendsome(&mensaje, sizeof(mensajes::mensajes_local_broker_token_t));
	} catch (OSError & error) {
	}
}
예제 #2
0
/**
 * main routine for receiver process.
 *
 * Receiver process runs on the same host as the generator process. */
int recv_files(int f_in, char *local_name)
{
	int fd1,fd2;
	STRUCT_STAT st;
	int iflags, xlen;
	char *fname, fbuf[MAXPATHLEN];
	char xname[MAXPATHLEN];
	char fnametmp[MAXPATHLEN];
	char *fnamecmp, *partialptr;
	char fnamecmpbuf[MAXPATHLEN];
	uchar fnamecmp_type;
	struct file_struct *file;
	struct stats initial_stats;
	int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i;
	enum logcode log_code = log_before_transfer ? FLOG : FINFO;
	int max_phase = protocol_version >= 29 ? 2 : 1;
	int dflt_perms = (ACCESSPERMS & ~orig_umask);
#if 0 /* was SUPPORT_ACLS */
	const char *parent_dirname = "";
#endif
	int ndx, recv_ok;

	if (verbose > 2)
		rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used);

	if (delay_updates)
		delayed_bits = bitbag_create(cur_flist->used + 1);

	while (1) {
                struct sum_struct sum;
                int numMatchTokens = -1, nextToken = 0;
                char *nextData = NULL;
                static char file_sum[MAX_DIGEST_LEN];

		cleanup_disable();

		/* This call also sets cur_flist. */
		ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
					 xname, &xlen);
		if (ndx == NDX_DONE) {
			if (inc_recurse && first_flist) {
				if (read_batch)
					gen_wants_ndx(first_flist->used + first_flist->ndx_start);
				flist_free(first_flist);
				if (first_flist)
					continue;
			} else if (read_batch && first_flist)
				gen_wants_ndx(first_flist->used);
			if (++phase > max_phase)
				break;
			if (verbose > 2)
				rprintf(FINFO, "recv_files phase=%d\n", phase);
			if (phase == 2 && delay_updates)
				handle_delayed_updates(local_name);
			send_msg(MSG_DONE, "", 0, 0);
			continue;
		}

		if (ndx - cur_flist->ndx_start >= 0)
			file = cur_flist->files[ndx - cur_flist->ndx_start];
		else
			file = dir_flist->files[cur_flist->parent_ndx];
		fname = local_name ? local_name : f_name(file, fbuf);

		if (verbose > 2)
			rprintf(FINFO, "recv_files(%s)\n", fname);

#ifdef SUPPORT_XATTRS
		if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers)
			recv_xattr_request(file, f_in);
#endif

		if (!(iflags & ITEM_TRANSFER)) {
			maybe_log_item(file, iflags, itemizing, xname);
#ifdef SUPPORT_XATTRS
			if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers
			 && !BITS_SET(iflags, ITEM_XNAME_FOLLOWS|ITEM_LOCAL_CHANGE))
				set_file_attrs(fname, file, NULL, fname, 0);
#endif
			continue;
		}
		if (phase == 2) {
			rprintf(FERROR,
				"got transfer request in phase 2 [%s]\n",
				who_am_i());
			exit_cleanup(RERR_PROTOCOL);
		}

		if (file->flags & FLAG_FILE_SENT) {
			if (csum_length == SHORT_SUM_LENGTH) {
				if (keep_partial && !partial_dir)
					make_backups = -make_backups; /* prevents double backup */
				if (append_mode)
					sparse_files = -sparse_files;
				append_mode = -append_mode;
				csum_length = SUM_LENGTH;
				redoing = 1;
			}
		} else {
			if (csum_length != SHORT_SUM_LENGTH) {
				if (keep_partial && !partial_dir)
					make_backups = -make_backups;
				if (append_mode)
					sparse_files = -sparse_files;
				append_mode = -append_mode;
				csum_length = SHORT_SUM_LENGTH;
				redoing = 0;
			}
		}

		if (!am_server && do_progress)
			set_current_file_index(file, ndx);
		stats.num_transferred_files++;
		stats.total_transferred_size += F_LENGTH(file);

		cleanup_got_literal = 0;

		if (daemon_filter_list.head
		    && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) {
			rprintf(FERROR, "attempt to hack rsync failed.\n");
			exit_cleanup(RERR_PROTOCOL);
		}

                read_sum_head(f_in, &sum);

		if (read_batch) {
			int wanted = redoing
				   ? we_want_redo(ndx)
				   : gen_wants_ndx(ndx);
			if (!wanted) {
				rprintf(FINFO,
					"(Skipping batched update for%s \"%s\")\n",
					redoing ? " resend of" : "",
					fname);
				discard_receive_data(f_in, F_LENGTH(file),
                                                     &sum, numMatchTokens, nextToken, nextData, file_sum);
				file->flags |= FLAG_FILE_SENT;
				continue;
			}
		}

		if (!do_xfers) { /* log the transfer */
			log_item(FCLIENT, file, &stats, iflags, NULL);
			if (read_batch)
				discard_receive_data(f_in, F_LENGTH(file),
                                                     &sum, numMatchTokens, nextToken, nextData, file_sum);
			continue;
		}
		if (write_batch < 0) {
			log_item(FCLIENT, file, &stats, iflags, NULL);
			if (!am_server)
				discard_receive_data(f_in, F_LENGTH(file),
                                                     &sum, numMatchTokens, nextToken, nextData, file_sum);
			continue;
		}

		partialptr = partial_dir ? partial_dir_fname(fname) : fname;

		if (protocol_version >= 29) {
			switch (fnamecmp_type) {
			case FNAMECMP_FNAME:
				fnamecmp = fname;
				break;
			case FNAMECMP_PARTIAL_DIR:
				fnamecmp = partialptr;
				break;
			case FNAMECMP_BACKUP:
				fnamecmp = get_backup_name(fname);
				break;
			case FNAMECMP_FUZZY:
				if (file->dirname) {
					pathjoin(fnamecmpbuf, MAXPATHLEN,
						 file->dirname, xname);
					fnamecmp = fnamecmpbuf;
				} else
					fnamecmp = xname;
				break;
			default:
				if (fnamecmp_type >= basis_dir_cnt) {
					rprintf(FERROR,
						"invalid basis_dir index: %d.\n",
						fnamecmp_type);
					exit_cleanup(RERR_PROTOCOL);
				}
				pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
					 basis_dir[fnamecmp_type], fname);
				fnamecmp = fnamecmpbuf;
				break;
			}
			if (!fnamecmp || (daemon_filter_list.head
			  && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0)) {
				fnamecmp = fname;
				fnamecmp_type = FNAMECMP_FNAME;
			}
		} else {
			/* Reminder: --inplace && --partial-dir are never
			 * enabled at the same time. */
			if (inplace && make_backups > 0) {
				if (!(fnamecmp = get_backup_name(fname)))
					fnamecmp = fname;
				else
					fnamecmp_type = FNAMECMP_BACKUP;
			} else if (partial_dir && partialptr)
				fnamecmp = partialptr;
			else
				fnamecmp = fname;
		}

		initial_stats = stats;

                /*
                 * Opening/reading/writing files in BackupPC is quite expensive, given the
                 * compression.  Before we open the file and call mkstemp, we peek ahead at
                 * the delta tokens to see if the file is unchanged.  If so, we skip the file
                 * open, mkstemp and writing.  The file is unchanged if the tokens
                 * are sequential matching block numbers, which are encoded as
                 * negative integers (ie: -1, -2, -3...), with no literal data
                 * tokens (which have positive numbers).
                 *
                 * If the file has changed, at some point a matching block token
                 * will be out of order, or we will encounter a literal data token.
                 * At that point, we stop reading ahead and proceed as normal.
                 * We pass the number of matching block tokens, and the first
                 * unexpected token, to receive_data(), so it can process all
                 * the tokens back (basically playing them back).
                 *
                 * Of course, this only applies if the file exists already,
                 * and protocol_version 30 is required since older protocols
                 * use MD4 for the full-file digest, not MD5.
                 *
                 * First we need to read the sum struct, then start reading the
                 * tokens.
                 */
                recv_ok = 0;
                if ( protocol_version >= 30 ) {
                    numMatchTokens = 0;
                    while ( (nextToken = recv_token(f_in, &nextData)) != 0 ) {
                        if ( nextToken != -numMatchTokens - 1 ) break;
                        numMatchTokens++;
                    }
                    if ( nextToken == 0 ) {
                        OFF_T flength = (OFF_T)numMatchTokens * sum.blength;
                        if ( sum.remainder && numMatchTokens > 0 )
                                flength -= sum.blength - sum.remainder;
                        /*
                         * Likely exact match - read the final file digest and make
                         * sure the digest and file size match the existing file.
                         * If so, this call creates the temporary file so the
                         * attributes can be checked/set, and then renamed as
                         * before.
                         */
                        read_buf(f_in, file_sum, MD5_DIGEST_LEN);
                        if ( !bpc_sysCall_checkFileMatch(fnamecmp, fnametmp, file, file_sum, flength) ) {
                            recv_ok = 1;
                            if ( log_before_transfer ) {
                                iflags &= ~ITEM_REPORT_CHANGE;
                                log_item(FCLIENT, file, &initial_stats, iflags, NULL);
                            }
                        }
                    }
                }
                if ( !recv_ok ) {
                    /*
                     * proceed as normal, remembering to replay the tokens we read ahead above:
                     * first, numMatchTokens: -1, -2, ..., -numMatchTokens,
                     * then (nextToken,nextData).  After that we go back to reading the
                     * remaining tokens from f_in.
                     */

                    /* open the file */
                    fd1 = do_open(fnamecmp, O_RDONLY, 0);

                    if ( fd1 < 0 && protocol_version >= 30 && always_checksum ) {
                        /*
                         * For protocol_version >= 30 and if always_checksum is set, we can use the
                         * MD5 whole-file digest to check for a potential match via the pool.
                         * Use that as the basis if the file is there.  The generator does
                         * the same in recv_generator().
                         */
                        if ( S_ISREG(file->mode) && !bpc_sysCall_poolFileCheck(fnamecmp, file) ) {
                            fd1 = do_open(fnamecmp, O_RDONLY, 0);
                        }
                    }

                    if (fd1 == -1 && protocol_version < 29) {
                            if (fnamecmp != fname) {
                                    fnamecmp = fname;
                                    fd1 = do_open(fnamecmp, O_RDONLY, 0);
                            }

                            if (fd1 == -1 && basis_dir[0]) {
                                    /* pre-29 allowed only one alternate basis */
                                    pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
                                             basis_dir[0], fname);
                                    fnamecmp = fnamecmpbuf;
                                    fd1 = do_open(fnamecmp, O_RDONLY, 0);
                            }
                    }

                    updating_basis_or_equiv = inplace
                        && (fnamecmp == fname || fnamecmp_type == FNAMECMP_BACKUP);

                    if (fd1 == -1) {
                            st.st_mode = 0;
                            st.st_size = 0;
                    } else if (do_fstat(fd1,&st) != 0) {
                            rsyserr(FERROR_XFER, errno, "fstat %s failed",
                                    full_fname(fnamecmp));
                            discard_receive_data(f_in, F_LENGTH(file),
                                                 &sum, numMatchTokens, nextToken, nextData, file_sum);
                            bpc_close(fd1);
                            if (inc_recurse)
                                    send_msg_int(MSG_NO_SEND, ndx);
                            continue;
                    }

                    if (fd1 != -1 && S_ISDIR(st.st_mode) && fnamecmp == fname) {
                            /* this special handling for directories
                             * wouldn't be necessary if robust_rename()
                             * and the underlying robust_unlink could cope
                             * with directories
                             */
                            rprintf(FERROR_XFER, "recv_files: %s is a directory\n",
                                    full_fname(fnamecmp));
                            discard_receive_data(f_in, F_LENGTH(file),
                                                 &sum, numMatchTokens, nextToken, nextData, file_sum);
                            bpc_close(fd1);
                            if (inc_recurse)
                                    send_msg_int(MSG_NO_SEND, ndx);
                            continue;
                    }

                    if (fd1 != -1 && !S_ISREG(st.st_mode)) {
                            bpc_close(fd1);
                            fd1 = -1;
                    }

                    /* If we're not preserving permissions, change the file-list's
                     * mode based on the local permissions and some heuristics. */
                    if (!preserve_perms) {
                            int exists = fd1 != -1;
    #if 0 /* was SUPPORT_ACLS */
                            const char *dn = file->dirname ? file->dirname : ".";
                            if (parent_dirname != dn
                             && strcmp(parent_dirname, dn) != 0) {
                                    dflt_perms = default_perms_for_dir(dn);
                                    parent_dirname = dn;
                            }
    #endif
                            file->mode = dest_mode(file->mode, st.st_mode,
                                                   dflt_perms, exists);
                    }

                    /* We now check to see if we are writing the file "inplace" */
                    if (inplace)  {
                            fd2 = do_open(fname, O_WRONLY|O_CREAT, 0600);
                            if (fd2 == -1) {
                                    rsyserr(FERROR_XFER, errno, "open %s failed",
                                            full_fname(fname));
                            }
                    } else {
                            fd2 = open_tmpfile(fnametmp, fname, file);
                            if (fd2 != -1)
                                    cleanup_set(fnametmp, partialptr, file, fd1, fd2);
                    }

                    if (fd2 == -1) {
                            discard_receive_data(f_in, F_LENGTH(file),
                                                 &sum, numMatchTokens, nextToken, nextData, file_sum);
                            if (fd1 != -1)
                                    bpc_close(fd1);
                            if (inc_recurse)
                                    send_msg_int(MSG_NO_SEND, ndx);
                            continue;
                    }

                    /* log the transfer */
                    if (log_before_transfer)
                            log_item(FCLIENT, file, &initial_stats, iflags, NULL);
                    else if (!am_server && verbose && do_progress)
                            rprintf(FINFO, "%s\n", fname);

                    /* recv file data */
                    recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size,
                                           fname, fd2, F_LENGTH(file),
                                           &sum, numMatchTokens, nextToken, nextData, file_sum);

                    if (fd1 != -1)
                            bpc_close(fd1);
                    if (bpc_close(fd2) < 0) {
                            rsyserr(FERROR, errno, "close failed on %s",
                                    full_fname(fnametmp));
                            exit_cleanup(RERR_FILEIO);
                    }
                }

                log_item(log_code, file, &initial_stats, iflags, NULL);

		if ((recv_ok && (!delay_updates || !partialptr)) || inplace) {
			if (partialptr == fname)
				partialptr = NULL;
			if (!finish_transfer(fname, fnametmp, fnamecmp,
					     partialptr, file, recv_ok, 1))
				recv_ok = -1;
			else if (fnamecmp == partialptr) {
				do_unlink(partialptr);
				handle_partial_dir(partialptr, PDIR_DELETE);
			}
		} else if (keep_partial && partialptr) {
			if (!handle_partial_dir(partialptr, PDIR_CREATE)) {
				rprintf(FERROR,
				    "Unable to create partial-dir for %s -- discarding %s.\n",
				    local_name ? local_name : f_name(file, NULL),
				    recv_ok ? "completed file" : "partial file");
				do_unlink(fnametmp);
				recv_ok = -1;
			} else if (!finish_transfer(partialptr, fnametmp, fnamecmp, NULL,
						    file, recv_ok, !partial_dir))
				recv_ok = -1;
			else if (delay_updates && recv_ok) {
				bitbag_set_bit(delayed_bits, ndx);
				recv_ok = 2;
			} else
				partialptr = NULL;
		} else
			do_unlink(fnametmp);

		cleanup_disable();

		if (read_batch)
			file->flags |= FLAG_FILE_SENT;

		switch (recv_ok) {
		case 2:
			break;
		case 1:
			if (remove_source_files || inc_recurse
			 || (preserve_hard_links && F_IS_HLINKED(file)))
				send_msg_int(MSG_SUCCESS, ndx);
			break;
		case 0: {
			enum logcode msgtype = redoing ? FERROR_XFER : FWARNING;
			if (msgtype == FERROR_XFER || verbose) {
				char *errstr, *redostr, *keptstr;
				if (!(keep_partial && partialptr) && !inplace)
					keptstr = "discarded";
				else if (partial_dir)
					keptstr = "put into partial-dir";
				else
					keptstr = "retained";
				if (msgtype == FERROR_XFER) {
					errstr = "ERROR";
					redostr = "";
				} else {
					errstr = "WARNING";
					redostr = read_batch ? " (may try again)"
							     : " (will try again)";
				}
				rprintf(msgtype,
					"%s: %s failed verification -- update %s%s.\n",
					errstr, local_name ? f_name(file, NULL) : fname,
					keptstr, redostr);
			}
			if (!redoing) {
				if (read_batch)
					flist_ndx_push(&batch_redo_list, ndx);
				send_msg_int(MSG_REDO, ndx);
				file->flags |= FLAG_FILE_SENT;
                                bpc_sysCall_printfileStatus(fname, "retry");
			} else if (inc_recurse)
				send_msg_int(MSG_NO_SEND, ndx);
                                bpc_sysCall_printfileStatus(fname, "fail");
			break;
		    }
		case -1:
			if (inc_recurse)
				send_msg_int(MSG_NO_SEND, ndx);
			break;
		}
	}
	if (make_backups < 0)
		make_backups = -make_backups;

	if (phase == 2 && delay_updates) /* for protocol_version < 29 */
		handle_delayed_updates(local_name);

	if (verbose > 2)
		rprintf(FINFO,"recv_files finished\n");

	return 0;
}
예제 #3
0
static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
			const char *fname, int fd, OFF_T total_size)
{
	static char file_sum1[MAX_DIGEST_LEN];
	static char file_sum2[MAX_DIGEST_LEN];
	struct map_struct *mapbuf;
	struct sum_struct sum;
	int32 len, sum_len;
	OFF_T offset = 0;
	OFF_T offset2;
	char *data;
	int32 i;
	char *map = NULL;

	read_sum_head(f_in, &sum);

	if (fd_r >= 0 && size_r > 0) {
		int32 read_size = MAX(sum.blength * 2, 16*1024);
		mapbuf = map_file(fd_r, size_r, read_size, sum.blength);
		if (verbose > 2) {
			rprintf(FINFO, "recv mapped %s of size %.0f\n",
				fname_r, (double)size_r);
		}
	} else
		mapbuf = NULL;

	sum_init(checksum_seed);

	if (append_mode > 0) {
		OFF_T j;
		sum.flength = (OFF_T)sum.count * sum.blength;
		if (sum.remainder)
			sum.flength -= sum.blength - sum.remainder;
		if (append_mode == 2) {
			for (j = CHUNK_SIZE; j < sum.flength; j += CHUNK_SIZE) {
				if (do_progress)
					show_progress(offset, total_size);
				sum_update(map_ptr(mapbuf, offset, CHUNK_SIZE),
					   CHUNK_SIZE);
				offset = j;
			}
			if (offset < sum.flength) {
				int32 len = (int32)(sum.flength - offset);
				if (do_progress)
					show_progress(offset, total_size);
				sum_update(map_ptr(mapbuf, offset, len), len);
			}
		}
		offset = sum.flength;
		if (fd != -1 && (j = do_lseek(fd, offset, SEEK_SET)) != offset) {
			rsyserr(FERROR_XFER, errno, "lseek of %s returned %.0f, not %.0f",
				full_fname(fname), (double)j, (double)offset);
			exit_cleanup(RERR_FILEIO);
		}
	}

	while ((i = recv_token(f_in, &data)) != 0) {
		if (do_progress)
			show_progress(offset, total_size);

		if (i > 0) {
			if (verbose > 3) {
				rprintf(FINFO,"data recv %d at %.0f\n",
					i,(double)offset);
			}

			stats.literal_data += i;
			cleanup_got_literal = 1;

			sum_update(data, i);

			if (fd != -1 && write_file(fd,data,i) != i)
				goto report_write_error;
			offset += i;
			continue;
		}

		i = -(i+1);
		offset2 = i * (OFF_T)sum.blength;
		len = sum.blength;
		if (i == (int)sum.count-1 && sum.remainder != 0)
			len = sum.remainder;

		stats.matched_data += len;

		if (verbose > 3) {
			rprintf(FINFO,
				"chunk[%d] of size %ld at %.0f offset=%.0f\n",
				i, (long)len, (double)offset2, (double)offset);
		}

		if (mapbuf) {
			map = map_ptr(mapbuf,offset2,len);

			see_token(map, len);
			sum_update(map, len);
		}

		if (updating_basis_or_equiv) {
			if (offset == offset2 && fd != -1) {
				OFF_T pos;
				if (flush_write_file(fd) < 0)
					goto report_write_error;
				offset += len;
				if ((pos = do_lseek(fd, len, SEEK_CUR)) != offset) {
					rsyserr(FERROR_XFER, errno,
						"lseek of %s returned %.0f, not %.0f",
						full_fname(fname),
						(double)pos, (double)offset);
					exit_cleanup(RERR_FILEIO);
				}
				continue;
			}
		}
		if (fd != -1 && map && write_file(fd, map, len) != (int)len)
			goto report_write_error;
		offset += len;
	}

	if (flush_write_file(fd) < 0)
		goto report_write_error;

#ifdef HAVE_FTRUNCATE
	if (inplace && fd != -1
	 && ftruncate(fd, offset) < 0) {
		rsyserr(FERROR_XFER, errno, "ftruncate failed on %s",
			full_fname(fname));
	}
#endif

	if (do_progress)
		end_progress(total_size);

	if (fd != -1 && offset > 0 && sparse_end(fd) != 0) {
	    report_write_error:
		rsyserr(FERROR_XFER, errno, "write failed on %s",
			full_fname(fname));
		exit_cleanup(RERR_FILEIO);
	}

	sum_len = sum_end(file_sum1);

	if (mapbuf)
		unmap_file(mapbuf);

	read_buf(f_in, file_sum2, sum_len);
	if (verbose > 2)
		rprintf(FINFO,"got file_sum\n");
	if (fd != -1 && memcmp(file_sum1, file_sum2, sum_len) != 0)
		return 0;
	return 1;
}
예제 #4
0
static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
			const char *fname, int fd, OFF_T total_size,
                        struct sum_struct *sum, int numMatchTokens,
                        int nextToken, char *nextData,
                        char *file_sum2)
{
	static char file_sum1[MAX_DIGEST_LEN];
	struct map_struct *mapbuf;
	int32 len, sum_len;
	OFF_T offset = 0;
	OFF_T offset2;
        int   offsetDefer;
	char *data;
	int32 i;
	char *map = NULL;
        int replayTokenCnt = 0;

	if (fd_r >= 0 && size_r > 0) {
		int32 read_size = MAX(sum->blength * 2, 16*1024);
		mapbuf = map_file(fd_r, size_r, read_size, sum->blength);
		if (verbose > 2) {
			rprintf(FINFO, "recv mapped %s of size %.0f\n",
				fname_r, (double)size_r);
		}
	} else
		mapbuf = NULL;

	sum_init(checksum_seed);

	if (append_mode > 0) {
		OFF_T j;
		sum->flength = (OFF_T)sum->count * sum->blength;
		if (sum->remainder)
			sum->flength -= sum->blength - sum->remainder;
		if (append_mode == 2) {
			for (j = CHUNK_SIZE; j < sum->flength; j += CHUNK_SIZE) {
				if (do_progress)
					show_progress(offset, total_size);
				sum_update(map_ptr(mapbuf, offset, CHUNK_SIZE),
					   CHUNK_SIZE);
				offset = j;
			}
			if (offset < sum->flength) {
				int32 len = (int32)(sum->flength - offset);
				if (do_progress)
					show_progress(offset, total_size);
				sum_update(map_ptr(mapbuf, offset, len), len);
			}
		}
		offset = sum->flength;
		if (fd != -1 && (j = do_lseek(fd, offset, SEEK_SET)) != offset) {
			rsyserr(FERROR_XFER, errno, "lseek of %s returned %.0f, not %.0f",
				full_fname(fname), (double)j, (double)offset);
			exit_cleanup(RERR_FILEIO);
		}
	}

        offsetDefer = 0;
	while ( 1 ) {
                /*
                 * We have to replay any tokens that were potentially read-ahead
                 * to see if the file was identical.
                 *   numMatchTokens < 0  means there are no replay tokens
                 *   numMatchTokens >= 0 means there are numMatchTokens from -1
                 *                       to -numMatchTokens, followed by
                 *                       (nextToken, *nextData).
                 *
                 * If numMatchTokens >= 0 and nextToken == 0, then then file_sum
                 * was already ready from f_in.  Otherwise, we need to read it
                 * here.
                 */
                if ( replayTokenCnt >= 0 && numMatchTokens >= 0 ) {
                    if ( replayTokenCnt < numMatchTokens ) {
                        /*
                         * replay -1, -2, ..., -numMatchTokens
                         */
                        i = -replayTokenCnt - 1;
                        replayTokenCnt++;
                    } else {
                        /*
                         * replay the next token - after this we are
                         * up to date.
                         */
                        i = nextToken;
                        data = nextData;
                        replayTokenCnt = -1;
                    }
                } else {
                    i = recv_token(f_in, &data);
                }
                if ( i == 0 ) break;

		if (do_progress)
			show_progress(offset, total_size);

		if (i > 0) {
			if (verbose > 3) {
				rprintf(FINFO,"data recv %d at %.0f\n",
					i,(double)offset);
			}

			stats.literal_data += i;
			cleanup_got_literal = 1;

			sum_update(data, i);

                        if ( offsetDefer ) {
				OFF_T pos;
				if (flush_write_file(fd) < 0)
					goto report_write_error;
				if ((pos = do_lseek(fd, offset, SEEK_SET)) != offset) {
					rsyserr(FERROR_XFER, errno,
						"lseek of %s returned %.0f, not %.0f",
						full_fname(fname),
						(double)pos, (double)offset);
					exit_cleanup(RERR_FILEIO);
				}
                                offsetDefer = 0;
                        }
			if (fd != -1 && write_file(fd,data,i) != i)
				goto report_write_error;
			offset += i;
			continue;
		}

		i = -(i+1);
		offset2 = i * (OFF_T)sum->blength;
		len = sum->blength;
		if (i == (int)sum->count-1 && sum->remainder != 0)
			len = sum->remainder;

		stats.matched_data += len;

		if (verbose > 3) {
			rprintf(FINFO,
				"chunk[%d] of size %ld at %.0f offset=%.0f%s\n",
				i, (long)len, (double)offset2, (double)offset,
				updating_basis_or_equiv && offset == offset2 ? " (seek)" : "");
		}

		if (mapbuf) {
			map = map_ptr(mapbuf,offset2,len);

			see_token(map, len);
			sum_update(map, len);
		}

		if (updating_basis_or_equiv) {
			if (offset == offset2 && fd != -1) {
                                offset += len;
                                offsetDefer = 1;
                                continue;
                        }
                }
                if (fd != -1 && map) {
                         if ( offsetDefer ) {
				OFF_T pos;
				if (flush_write_file(fd) < 0)
					goto report_write_error;
				if ((pos = do_lseek(fd, offset, SEEK_SET)) != offset) {
					rsyserr(FERROR_XFER, errno,
						"lseek of %s returned %.0f, not %.0f",
						full_fname(fname),
						(double)pos, (double)offset);
					exit_cleanup(RERR_FILEIO);
				}
                                offsetDefer = 0;
                        }
                        if ( write_file(fd, map, len) != (int)len)
                                goto report_write_error;
		}
		offset += len;
	}
        if ( offsetDefer ) {
                OFF_T pos;
                if (flush_write_file(fd) < 0)
                        goto report_write_error;
                if ((pos = do_lseek(fd, offset, SEEK_SET)) != offset) {
                        rsyserr(FERROR_XFER, errno,
                                "lseek of %s returned %.0f, not %.0f",
                                full_fname(fname),
                                (double)pos, (double)offset);
                        exit_cleanup(RERR_FILEIO);
                }
                offsetDefer = 0;
        }

	if (flush_write_file(fd) < 0)
		goto report_write_error;

#ifdef HAVE_FTRUNCATE
	if (fd != -1 && do_ftruncate(fd, offset) < 0) {
		rsyserr(FERROR_XFER, errno, "ftruncate failed on %s",
			full_fname(fname));
	}
#endif

	if (do_progress)
		end_progress(total_size);

	if (fd != -1 && offset > 0 && sparse_end(fd, offset) != 0) {
	    report_write_error:
		rsyserr(FERROR_XFER, errno, "write failed on %s",
			full_fname(fname));
		exit_cleanup(RERR_FILEIO);
	}

        sum_len = sum_end(file_sum1);

	if (mapbuf)
		unmap_file(mapbuf);

        if ( numMatchTokens < 0 || nextToken != 0 ) {
            /*
             * If numMatchTokens >= 0 and nextToken == 0, then the caller already
             * read ahead to the digest.  Otherwise we have to read it here.
             */
            read_buf(f_in, file_sum2, sum_len);
        }
	if (verbose > 2)
		rprintf(FINFO,"got file_sum\n");
	if (fd != -1 && memcmp(file_sum1, file_sum2, sum_len) != 0)
		return 0;
	return 1;
}
예제 #5
0
/*
 * Function: client_establish_context
 *
 * Purpose: establishes a GSS-API context with a specified service and
 * returns the context handle
 *
 * Arguments:
 *
 *      s                   (r) an established TCP connection to the service
 *      service_name(r) the ASCII service name of the service
 *      gss_flags       (r) GSS-API delegation flag (if any)
 *      auth_flag       (r) whether to actually do authentication
 *  v1_format   (r) whether the v1 sample protocol should be used
 *      oid                 (r) OID of the mechanism to use
 *      context         (w) the established GSS-API context
 *      ret_flags       (w) the returned flags from init_sec_context
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * service_name is imported as a GSS-API name and a GSS-API context is
 * established with the corresponding service; the service should be
 * listening on the TCP connection s.  The default GSS-API mechanism
 * is used, and mutual authentication and replay detection are
 * requested.
 *
 * If successful, the context handle is returned in context.  If
 * unsuccessful, the GSS-API error messages are displayed on stderr
 * and -1 is returned.
 */
static int
client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
                         int auth_flag, int v1_format, gss_OID oid,
                         char *username, char *password,
                         gss_ctx_id_t *gss_context, OM_uint32 *ret_flags)
{
    if (auth_flag) {
        gss_buffer_desc send_tok, recv_tok, *token_ptr;
        gss_name_t target_name;
        OM_uint32 maj_stat, min_stat, init_sec_min_stat;
        int token_flags;
        gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
        gss_name_t gss_username = GSS_C_NO_NAME;
        gss_OID_set_desc mechs, *mechsp = GSS_C_NO_OID_SET;
#ifndef MECH_EAP
        struct gss_channel_bindings_struct cb = {GSS_C_AF_NULLADDR, {0, NULL},
                                                 GSS_C_AF_NULLADDR, {0, NULL},
            {strlen("HLJHLJHLJHLJHJKLHLJHLJH"), "HLJHLJHLJHLJHJKLHLJHLJH"}};
#endif

        if (spnego) {
            mechs.elements = &gss_spnego_mechanism_oid_desc;
            mechs.count = 1;
            mechsp = &mechs;
        } else if (oid != GSS_C_NO_OID) {
            mechs.elements = oid;
            mechs.count = 1;
            mechsp = &mechs;
        } else {
            mechs.elements = NULL;
            mechs.count = 0;
        }

        if (username != NULL) {
            send_tok.value = username;
            send_tok.length = strlen(username);

            maj_stat = gss_import_name(&min_stat, &send_tok,
                                       (gss_OID) gss_nt_user_name,
                                       &gss_username);
            if (maj_stat != GSS_S_COMPLETE) {
                display_status("parsing client name", maj_stat, min_stat);
                return -1;
            }
        }

        if (password != NULL) {
            gss_buffer_desc pwbuf;

            pwbuf.value = password;
            pwbuf.length = strlen(password);

            maj_stat = gss_acquire_cred_with_password(&min_stat,
                                                      gss_username,
                                                      &pwbuf, 0,
                                                      mechsp, GSS_C_INITIATE,
                                                      &cred, NULL, NULL);
        } else if (gss_username != GSS_C_NO_NAME) {
            maj_stat = gss_acquire_cred(&min_stat,
                                        gss_username, 0,
                                        mechsp, GSS_C_INITIATE,
                                        &cred, NULL, NULL);
        } else
            maj_stat = GSS_S_COMPLETE;
        if (maj_stat != GSS_S_COMPLETE) {
            display_status("acquiring creds", maj_stat, min_stat);
            gss_release_name(&min_stat, &gss_username);
            return -1;
        }
        if (spnego && oid != GSS_C_NO_OID) {
            gss_OID_set_desc neg_mechs;

            neg_mechs.elements = oid;
            neg_mechs.count = 1;

            maj_stat = gss_set_neg_mechs(&min_stat, cred, &neg_mechs);
            if (maj_stat != GSS_S_COMPLETE) {
                display_status("setting neg mechs", maj_stat, min_stat);
                gss_release_name(&min_stat, &gss_username);
                gss_release_cred(&min_stat, &cred);
                return -1;
            }
        }
        gss_release_name(&min_stat, &gss_username);

        /*
         * Import the name into target_name.  Use send_tok to save
         * local variable space.
         */
        send_tok.value = service_name;
        send_tok.length = strlen(service_name);
        maj_stat = gss_import_name(&min_stat, &send_tok,
                                   (gss_OID) gss_nt_service_name,
                                   &target_name);
        if (maj_stat != GSS_S_COMPLETE) {
            display_status("parsing name", maj_stat, min_stat);
            return -1;
        }

        if (!v1_format) {
            if (send_token(s, TOKEN_NOOP | TOKEN_CONTEXT_NEXT, empty_token) <
                0) {
                (void) gss_release_name(&min_stat, &target_name);
                return -1;
            }
        }

        /*
         * Perform the context-establishement loop.
         *
         * On each pass through the loop, token_ptr points to the token
         * to send to the server (or GSS_C_NO_BUFFER on the first pass).
         * Every generated token is stored in send_tok which is then
         * transmitted to the server; every received token is stored in
         * recv_tok, which token_ptr is then set to, to be processed by
         * the next call to gss_init_sec_context.
         *
         * GSS-API guarantees that send_tok's length will be non-zero
         * if and only if the server is expecting another token from us,
         * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
         * and only if the server has another token to send us.
         */

        token_ptr = GSS_C_NO_BUFFER;
        *gss_context = GSS_C_NO_CONTEXT;

        do {
            maj_stat = gss_init_sec_context(&init_sec_min_stat,
                                            cred, gss_context,
                                            target_name, mechs.elements,
                                            gss_flags, 0,
#ifdef MECH_EAP
                                            NULL, /* channel bindings */
#else
                                            &cb,
#endif
                                            token_ptr, NULL, /* mech type */
                                            &send_tok, ret_flags,
                                            NULL);  /* time_rec */

            if (token_ptr != GSS_C_NO_BUFFER)
                free(recv_tok.value);

            if (send_tok.length != 0) {
                if (verbose)
                    printf("Sending init_sec_context token (size=%d)...",
                           (int) send_tok.length);
                if (send_token(s, v1_format ? 0 : TOKEN_CONTEXT, &send_tok) <
                    0) {
                    (void) gss_release_buffer(&min_stat, &send_tok);
                    (void) gss_release_name(&min_stat, &target_name);
                    return -1;
                }
            }
            (void) gss_release_buffer(&min_stat, &send_tok);

            if (maj_stat != GSS_S_COMPLETE
                && maj_stat != GSS_S_CONTINUE_NEEDED) {
                display_status("initializing context", maj_stat,
                               init_sec_min_stat);
                (void) gss_release_name(&min_stat, &target_name);
                if (*gss_context != GSS_C_NO_CONTEXT)
                    gss_delete_sec_context(&min_stat, gss_context,
                                           GSS_C_NO_BUFFER);
                return -1;
            }

            if (maj_stat == GSS_S_CONTINUE_NEEDED) {
                if (verbose)
                    printf("continue needed...");
                if (recv_token(s, &token_flags, &recv_tok) < 0) {
                    (void) gss_release_name(&min_stat, &target_name);
                    return -1;
                }
                token_ptr = &recv_tok;
            }
            if (verbose)
                printf("\n");
        } while (maj_stat == GSS_S_CONTINUE_NEEDED);

        (void) gss_release_cred(&min_stat, &cred);
        (void) gss_release_name(&min_stat, &target_name);
    } else {
        if (send_token(s, TOKEN_NOOP, empty_token) < 0)
            return -1;
    }

    return 0;
}
예제 #6
0
/*
 * Function: server_establish_context
 *
 * Purpose: establishses a GSS-API context as a specified service with
 * an incoming client, and returns the context handle and associated
 * client name
 *
 * Arguments:
 *
 *      s               (r) an established TCP connection to the client
 *      service_creds   (r) server credentials, from gss_acquire_cred
 *      context         (w) the established GSS-API context
 *      client_name     (w) the client's ASCII name
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * Any valid client request is accepted.  If a context is established,
 * its handle is returned in context and the client name is returned
 * in client_name and 0 is returned.  If unsuccessful, an error
 * message is displayed and -1 is returned.
 */
static int
server_establish_context(int s, gss_cred_id_t server_creds,
                         gss_ctx_id_t *context, gss_buffer_t client_name,
                         OM_uint32 *ret_flags)
{
    gss_buffer_desc send_tok, recv_tok;
    gss_name_t client;
    gss_OID doid;
    OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
    gss_buffer_desc oid_name;
    int     token_flags;

    if (recv_token(s, &token_flags, &recv_tok) < 0)
        return -1;

    if (recv_tok.value) {
        free(recv_tok.value);
        recv_tok.value = NULL;
    }

    if (!(token_flags & TOKEN_NOOP)) {
        if (logfile)
            fprintf(logfile, "Expected NOOP token, got %d token instead\n",
                    token_flags);
        return -1;
    }

    *context = GSS_C_NO_CONTEXT;

    if (token_flags & TOKEN_CONTEXT_NEXT) {
        do {
            if (recv_token(s, &token_flags, &recv_tok) < 0)
                return -1;

            if (verbose && logfile) {
                fprintf(logfile, "Received token (size=%d): \n",
                        (int) recv_tok.length);
                print_token(&recv_tok);
            }

            maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context,
                                              server_creds, &recv_tok,
                                              GSS_C_NO_CHANNEL_BINDINGS,
                                              &client, &doid, &send_tok,
                                              ret_flags,
                                              NULL,  /* time_rec */
                                              NULL); /* del_cred_handle */

            if (recv_tok.value) {
                free(recv_tok.value);
                recv_tok.value = NULL;
            }

            if (send_tok.length != 0) {
                if (verbose && logfile) {
                    fprintf(logfile,
                            "Sending accept_sec_context token (size=%d):\n",
                            (int) send_tok.length);
                    print_token(&send_tok);
                }
                if (send_token(s, TOKEN_CONTEXT, &send_tok) < 0) {
                    if (logfile)
                        fprintf(logfile, "failure sending token\n");
                    return -1;
                }

                (void) gss_release_buffer(&min_stat, &send_tok);
            }
            if (maj_stat != GSS_S_COMPLETE
                && maj_stat != GSS_S_CONTINUE_NEEDED) {
                display_status("accepting context", maj_stat,
                               acc_sec_min_stat);
                if (*context != GSS_C_NO_CONTEXT)
                    gss_delete_sec_context(&min_stat, context,
                                           GSS_C_NO_BUFFER);
                return -1;
            }

            if (verbose && logfile) {
                if (maj_stat == GSS_S_CONTINUE_NEEDED)
                    fprintf(logfile, "continue needed...\n");
                else
                    fprintf(logfile, "\n");
                fflush(logfile);
            }
        } while (maj_stat == GSS_S_CONTINUE_NEEDED);

        /* display the flags */
        display_ctx_flags(*ret_flags);

        if (verbose && logfile) {
            maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name);
            if (maj_stat != GSS_S_COMPLETE) {
                display_status("converting oid->string", maj_stat, min_stat);
                return -1;
            }
            fprintf(logfile, "Accepted connection using mechanism OID %.*s.\n",
                    (int) oid_name.length, (char *) oid_name.value);
            (void) gss_release_buffer(&min_stat, &oid_name);
        }

        maj_stat = gss_display_name(&min_stat, client, client_name, &doid);
        if (maj_stat != GSS_S_COMPLETE) {
            display_status("displaying name", maj_stat, min_stat);
            return -1;
        }
        enumerateAttributes(&min_stat, client, TRUE);
        showLocalIdentity(&min_stat, client);
        maj_stat = gss_release_name(&min_stat, &client);
        if (maj_stat != GSS_S_COMPLETE) {
            display_status("releasing name", maj_stat, min_stat);
            return -1;
        }
    } else {
        client_name->length = *ret_flags = 0;

        if (logfile)
            fprintf(logfile, "Accepted unauthenticated connection.\n");
    }

    return 0;
}
예제 #7
0
/*
 * Establish a secure context using the GSS-API.  A handshake is attempted in
 * order to detect a non-authenticating server.  The server IP address is obtained
 * from the socket and is used to perform an fqdn lookup in case a DNS alias is
 * used as a host spec, this ensures we authenticate against the correct server.
 * We attempt to extract the server principal name, a service, from the
 * environment, if it is not specified we use "host/" as a default.
 *
 * @pram to_net_sd.	Socket to write to.
 *
 * @pram from_net_sd.	Socket to read from.
 *
 * @param req_flags.	A representation of the security services to
 *			be requested.
 *
 * @param ret_flags.    A representation of the security services
 *			provided/supported by the underlying mechanism
 *			to be returned to the invoking function.
 *
 * Returns 0 on success, otherwise error.
 */
static int dcc_gssapi_establish_secure_context(int to_net_sd,
					       int from_net_sd,
					       OM_uint32 req_flags,
					       OM_uint32 *ret_flags) {
    char *ext_princ_name = NULL;
    char *full_name = NULL;
    char *princ_env_val = NULL;
    gss_buffer_desc input_tok = GSS_C_EMPTY_BUFFER;
    gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER;
    gss_buffer_desc output_tok = GSS_C_EMPTY_BUFFER;
    gss_name_t int_serv_name;
    gss_OID name_type;
    int ret;
    OM_uint32 major_status, minor_status, return_status;
    socklen_t addr_len;
    struct hostent *hp;
    struct sockaddr_in addr;

    addr_len = sizeof(addr);

    if ((ret = getpeername(to_net_sd, &addr, &addr_len)) != 0) {
        rs_log_error("Failed to look up peer address using socket \"%d\": %s.",
                     to_net_sd,
                     hstrerror(h_errno));
        return EXIT_CONNECT_FAILED;
    }

    rs_log_info("Successfully looked up IP address %s using socket %d.",
                                                inet_ntoa(addr.sin_addr),
                                                to_net_sd);

    if ((hp = gethostbyaddr((char *) &addr.sin_addr,
                            sizeof(addr.sin_addr),
                            AF_INET)) == NULL) {
        rs_log_error("Failed to look up host by address \"%s\": %s.",
                     inet_ntoa(addr.sin_addr),
                     hstrerror(h_errno));
        return EXIT_CONNECT_FAILED;
    }

    rs_log_info("Successfully looked up host %s using IP address %s.",
                                                hp->h_name,
                                                inet_ntoa(addr.sin_addr));

    if ((full_name = malloc(strlen(hp->h_name) + 1)) == NULL) {
        rs_log_error("malloc failed : %ld bytes: out of memory.",
                                        (long) (strlen(hp->h_name) + 1));
        return EXIT_OUT_OF_MEMORY;
    }

    strcpy(full_name, hp->h_name);

    if ((princ_env_val = getenv("DISTCC_PRINCIPAL"))) {
        if (asprintf(&ext_princ_name, "%s@%s", princ_env_val, full_name) < 0) {
            rs_log_error("Failed to allocate memory for asprintf.");
            return EXIT_OUT_OF_MEMORY;
        }

        name_type = GSS_C_NT_HOSTBASED_SERVICE;
    } else {
        if (asprintf(&ext_princ_name, "host/%s", full_name) < 0) {
            rs_log_error("Failed to allocate memory for asprintf.");
            return EXIT_OUT_OF_MEMORY;
        }

        name_type = GSS_C_NT_USER_NAME;
    }

    free(full_name);
    name_buffer.value = ext_princ_name;
    name_buffer.length = strlen(ext_princ_name);

    if ((major_status = gss_import_name(&minor_status,
				       &name_buffer,
				       name_type,
				       &int_serv_name)) != GSS_S_COMPLETE) {
	rs_log_error("Failed to import service name to internal GSS-API format.");
        return EXIT_GSSAPI_FAILED;
    }

    input_tok.value = NULL;
    input_tok.length = 0;
    output_tok.value = NULL;
    output_tok.length = 0;

    if ((ret = dcc_gssapi_send_handshake(to_net_sd, from_net_sd)) != 0) {
        return ret;
    }

    do
    {
        major_status = gss_init_sec_context(&minor_status,
					    GSS_C_NO_CREDENTIAL,
					    &distcc_ctx_handle,
					    int_serv_name,
					    GSS_C_NO_OID,
					    req_flags,
					    0,
					    GSS_C_NO_CHANNEL_BINDINGS,
					    &input_tok,
					    NULL,
					    &output_tok,
					    ret_flags,
					    NULL);

	if (GSS_ERROR(major_status)) {
	    rs_log_crit("Failed to initiate a secure context.");
	    dcc_gssapi_status_to_log(major_status, GSS_C_GSS_CODE);
	    dcc_gssapi_status_to_log(minor_status, GSS_C_MECH_CODE);
	    return EXIT_GSSAPI_FAILED;
	}

	if (output_tok.length > 0) {
	    if ((ret = send_token(to_net_sd, &output_tok)) != 0) {
		dcc_gssapi_cleanup(&input_tok, &output_tok, &int_serv_name);
		return ret;
	    } else {
		if ((return_status = gss_release_buffer(&minor_status,
						 &output_tok)) != GSS_S_COMPLETE) {
		    rs_log_error("Failed to release buffer.");
		}
	    }
	}

	if (input_tok.length > 0) {
	    if ((return_status = gss_release_buffer(&minor_status,
						   &input_tok)) != GSS_S_COMPLETE) {
		rs_log_error("Failed to release buffer.");
	    }
	}

	if (major_status == GSS_S_CONTINUE_NEEDED) {
	    if ((ret = recv_token(from_net_sd, &input_tok)) != 0) {
		dcc_gssapi_cleanup(&input_tok, &output_tok, &int_serv_name);
		return ret;
	    }
	}

    } while (major_status != GSS_S_COMPLETE);

    rs_log_info("Successfully authenticated %s.", ext_princ_name);
    dcc_gssapi_cleanup(&input_tok, &output_tok, &int_serv_name);

    if ((major_status = gss_release_buffer(&minor_status,
					  &name_buffer)) != GSS_S_COMPLETE) {
	rs_log_error("Failed to release buffer.");
    }

    return 0;
}
예제 #8
0
파일: receiver.c 프로젝트: OPSF/uClinux
static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
			char *fname, int fd, OFF_T total_size)
{
	static char file_sum1[MD4_SUM_LENGTH];
	static char file_sum2[MD4_SUM_LENGTH];
	struct map_struct *mapbuf;
	struct sum_struct sum;
	int32 len;
	OFF_T offset = 0;
	OFF_T offset2;
	char *data;
	int32 i;
	char *map = NULL;

	read_sum_head(f_in, &sum);

	if (fd_r >= 0 && size_r > 0) {
		int32 read_size = MAX(sum.blength * 2, 16*1024);
		mapbuf = map_file(fd_r, size_r, read_size, sum.blength);
		if (verbose > 2) {
			rprintf(FINFO, "recv mapped %s of size %.0f\n",
				safe_fname(fname_r), (double)size_r);
		}
	} else
		mapbuf = NULL;

	sum_init(checksum_seed);

	while ((i = recv_token(f_in, &data)) != 0) {
		if (do_progress)
			show_progress(offset, total_size);

		if (i > 0) {
			if (verbose > 3) {
				rprintf(FINFO,"data recv %d at %.0f\n",
					i,(double)offset);
			}

			stats.literal_data += i;
			cleanup_got_literal = 1;

			sum_update(data, i);

			if (fd != -1 && write_file(fd,data,i) != i)
				goto report_write_error;
			offset += i;
			continue;
		}

		i = -(i+1);
		offset2 = i * (OFF_T)sum.blength;
		len = sum.blength;
		if (i == (int)sum.count-1 && sum.remainder != 0)
			len = sum.remainder;

		stats.matched_data += len;

		if (verbose > 3) {
			rprintf(FINFO,
				"chunk[%d] of size %ld at %.0f offset=%.0f\n",
				i, (long)len, (double)offset2, (double)offset);
		}

		if (mapbuf) {
			map = map_ptr(mapbuf,offset2,len);

			see_token(map, len);
			sum_update(map, len);
		}

		if (inplace) {
			if (offset == offset2 && fd != -1) {
				if (flush_write_file(fd) < 0)
					goto report_write_error;
				offset += len;
				if (do_lseek(fd, len, SEEK_CUR) != offset) {
					rsyserr(FERROR, errno,
						"lseek failed on %s",
						full_fname(fname));
					exit_cleanup(RERR_FILEIO);
				}
				continue;
			}
		}
		if (fd != -1 && map && write_file(fd, map, len) != (int)len)
			goto report_write_error;
		offset += len;
	}

	if (flush_write_file(fd) < 0)
		goto report_write_error;

#ifdef HAVE_FTRUNCATE
	if (inplace && fd != -1)
		ftruncate(fd, offset);
#endif

	if (do_progress)
		end_progress(total_size);

	if (fd != -1 && offset > 0 && sparse_end(fd) != 0) {
	    report_write_error:
		rsyserr(FERROR, errno, "write failed on %s",
			full_fname(fname));
		exit_cleanup(RERR_FILEIO);
	}

	sum_end(file_sum1);

	if (mapbuf)
		unmap_file(mapbuf);

	read_buf(f_in,file_sum2,MD4_SUM_LENGTH);
	if (verbose > 2)
		rprintf(FINFO,"got file_sum\n");
	if (fd != -1 && memcmp(file_sum1, file_sum2, MD4_SUM_LENGTH) != 0)
		return 0;
	return 1;
}