Пример #1
0
/*
 * Pretty printer
 */
void
ndmchan_pp (struct ndmchan *ch, char *buf)
{
	int		show_ra = 0;
	char *		bp = buf;
	char *		p;

	sprintf (bp, "name=%s", ch->name); while (*bp) bp++;

	switch (ch->mode) {
	case NDMCHAN_MODE_IDLE:		p = "idle"; break;
	case NDMCHAN_MODE_RESIDENT:	p = "resident"; show_ra = 1; break;
	case NDMCHAN_MODE_READ:		p = "read"; show_ra = 1; break;
	case NDMCHAN_MODE_WRITE:	p = "write"; show_ra = 1; break;
	case NDMCHAN_MODE_READCHK:	p = "readchk"; break;
	case NDMCHAN_MODE_LISTEN:	p = "listen"; break;
	case NDMCHAN_MODE_PENDING:	p = "pending"; break;
	case NDMCHAN_MODE_CLOSED:	p = "closed"; break;
	default:			p = "mode=???"; break;
	}

	sprintf (bp, " %s ", p);
	while (*bp) bp++;

	if (show_ra) {
		sprintf (bp, "ready=%d avail=%d ",
			ndmchan_n_ready(ch), ndmchan_n_avail(ch));
		while (*bp) bp++;
	}

	if (ch->ready)	strcat (bp, "-rdy");
	if (ch->check)	strcat (bp, "-chk");
	if (ch->eof)	strcat (bp, "-eof");
	if (ch->error)	strcat (bp, "-err");
}
Пример #2
0
enum ndmchan_read_interpretation
ndmchan_read_interpret (struct ndmchan *ch, char **data_p,
  unsigned *n_ready_p)
{
	unsigned	n_ready;

	n_ready = *n_ready_p = ndmchan_n_ready (ch);
	*data_p = &ch->data[ch->beg_ix];

	if (ch->error) {
		if (n_ready == 0) {
			return NDMCHAN_RI_DONE_ERROR;
		} else {
			return NDMCHAN_RI_DRAIN_ERROR;
		}
	}

	if (ch->eof) {
		if (n_ready == 0) {
			return NDMCHAN_RI_DONE_EOF;
		} else {
			return NDMCHAN_RI_DRAIN_EOF;
		}
	}

	if (n_ready == 0) {
		return NDMCHAN_RI_EMPTY;
	}

	if (n_ready == ch->data_size) {
		return NDMCHAN_RI_READY_FULL;
	}

	return NDMCHAN_RI_READY;
}
Пример #3
0
int
ndmda_quantum_stderr (struct ndm_session *sess)
{
	struct ndm_data_agent *	da = sess->data_acb;
	struct ndmchan *	ch = &da->formatter_error;
	int			did_something = 0;
	char *			p;
	char *			data;
	char *			pend;
	unsigned		n_ready;

  again:
	n_ready = ndmchan_n_ready (ch);
	if (n_ready == 0)
		return did_something;

	data = p = &ch->data[ch->beg_ix];
	pend = p + n_ready;

	while (p < pend && *p != '\n') p++;


	if (p < pend && *p == '\n') {
		*p++ = 0;
		ndma_send_logmsg (sess, NDMP9_LOG_NORMAL,  sess->plumb.data,
			"%s", data);
		ch->beg_ix += p - data;
		did_something++;
		goto again;
	}

	if (!ch->eof)
		return did_something;

	/* content w/o newline, and EOF */
	/* p == pend */
	if (ch->end_ix >= ch->data_size) {
		if (data != ch->data) {
			ndmchan_compress (ch);
			goto again;
		}
		/* that's one huge message */
		p--;	/* lose last byte */
	}

	ch->data[ch->end_ix++] = '\n';
	did_something++;
	goto again;
}
Пример #4
0
int
ndmchan_pre_poll (struct ndmchan *chtab[], unsigned n_chtab)
{
	struct ndmchan *	ch;
	unsigned int		i, n_check;

	n_check = 0;
	for (i = 0; i < n_chtab; i++) {
		ch = chtab[i];
		ch->ready = 0;
		ch->check = 0;

		if (ch->error)
			continue;

		switch (ch->mode) {
		default:
		case NDMCHAN_MODE_IDLE:
		case NDMCHAN_MODE_PENDING:
		case NDMCHAN_MODE_RESIDENT:
		case NDMCHAN_MODE_CLOSED:
			continue;

		case NDMCHAN_MODE_LISTEN:
		case NDMCHAN_MODE_READCHK:
			break;

		case NDMCHAN_MODE_READ:
			if (ch->eof)
				continue;
			if (ndmchan_n_avail (ch) == 0)
				continue;
			break;

		case NDMCHAN_MODE_WRITE:
			if (ndmchan_n_ready (ch) == 0)
				continue;
			break;
		}

		ch->check = 1;
		n_check++;
	}

	return n_check;
}
Пример #5
0
int
ndmta_write_quantum (struct ndm_session *sess)
{
	struct ndm_tape_agent *	ta = &sess->tape_acb;
	struct ndmchan *	ch = &sess->plumb.image_stream.chan;
	unsigned long		count = ta->mover_state.record_size;
	int			did_something = 0;
	unsigned long long	max_read;
	unsigned long long	want_window_off;
	unsigned long		want_blockno;
	unsigned long		cur_blockno;
	unsigned		n_avail, n_read, record_off;
	char *			data;
	unsigned long		done_count;
	ndmp9_error		error;

  again:
	n_read = n_avail = ndmchan_n_avail (ch);
	if (n_avail == 0) {
		/* allow to drain */
		return did_something;
	}

	if (ta->pending_change_after_drain) {
		if (ndmchan_n_ready (ch) > 0) {
			/* allow to drain */
		} else {
			ndmta_mover_apply_pending (sess);
			did_something++;
		}
		return did_something;
	}

	if (n_read > ta->mover_state.bytes_left_to_read)
		n_read = ta->mover_state.bytes_left_to_read;

	if (n_read == 0) {
		/* Active, but paused awaiting MOVER_READ request */
		return did_something;	/* mover blocked */
	}

	if (ta->mover_want_pos < ta->mover_state.window_offset
	 || ta->mover_want_pos >= ta->mover_window_end) {
		ndmta_mover_pause_pending (sess, NDMP9_MOVER_PAUSE_SEEK);
		goto again;
	}

	max_read = ta->mover_window_end - ta->mover_want_pos;
	if (n_read > max_read)
		n_read = max_read;

	want_window_off = ta->mover_want_pos - ta->mover_state.window_offset;

	want_blockno = want_window_off / ta->mover_state.record_size;

	if (ta->tb_blockno != want_blockno) {
		unsigned long	xsr_count, xsr_resid;

		ndmos_tape_sync_state(sess);
		cur_blockno = ta->tape_state.blockno.value;
		if (cur_blockno < want_blockno) {
			xsr_count = want_blockno - cur_blockno;
			error = ndmos_tape_mtio (sess, NDMP9_MTIO_FSR,
						xsr_count, &xsr_resid);
			if (error == NDMP9_EOF_ERR) {
				ndmta_mover_pause_pending (sess,
						NDMP9_MOVER_PAUSE_EOF);
				goto again;
			}
			if (error != NDMP9_NO_ERR) {
				ndmta_mover_pause_pending (sess,
						NDMP9_MOVER_PAUSE_MEDIA_ERROR);
				goto again;
			}
			if (xsr_resid > 0) {
				ndmta_mover_pause_pending (sess,
						NDMP9_MOVER_PAUSE_EOF);
				goto again;
			}
		} else if (cur_blockno > want_blockno) {
			xsr_count = cur_blockno - want_blockno;
			error = ndmos_tape_mtio (sess, NDMP9_MTIO_BSR,
						xsr_count, &xsr_resid);
			if (error != NDMP9_NO_ERR || xsr_resid > 0) {
				ndmta_mover_pause_pending (sess,
						NDMP9_MOVER_PAUSE_MEDIA_ERROR);
				goto again;
			}
		} else {
			/* in position */
		}

		data = ta->tape_buffer;
		done_count = 0;
		error = ndmos_tape_read (sess, data, count, &done_count);
		did_something++;

		if (error == NDMP9_EOF_ERR) {
			ndmta_mover_pause_pending (sess,
						NDMP9_MOVER_PAUSE_EOF);
			goto again;
		}
		if (error != NDMP9_NO_ERR) {
			ndmta_mover_pause_pending (sess,
				NDMP9_MOVER_PAUSE_MEDIA_ERROR);
			goto again;
		}
		if (done_count == 0) {
			return did_something - 1;
		}
		if (done_count != count) {
			n_read = done_count;
			goto aaa;
			ndmta_mover_pause_pending (sess,
				NDMP9_MOVER_PAUSE_MEDIA_ERROR);
			goto again;
		}
		ta->tb_blockno = want_blockno;
	}

aaa:
	record_off = ta->mover_want_pos % ta->mover_state.record_size;

	n_avail = ta->mover_state.record_size - record_off;
	if (n_read > n_avail)
		n_read = n_avail;

	data = &ta->tape_buffer[record_off];

	bcopy (data, ch->data + ch->end_ix, n_read);
	ch->end_ix += n_read;
	ta->mover_want_pos += n_read;
	ta->mover_state.bytes_left_to_read -= n_read;

	did_something++;

	goto again;	/* do as much as possible */
}
Пример #6
0
int
ndmta_read_quantum (struct ndm_session *sess)
{
	struct ndm_tape_agent *	ta = &sess->tape_acb;
	struct ndmchan *	ch = &sess->plumb.image_stream.chan;
	unsigned long		count = ta->mover_state.record_size;
	int			did_something = 0;
	unsigned		n_ready;
	char *			data;
	unsigned long		done_count;
	ndmp9_error		error;

  again:
	n_ready = ndmchan_n_ready (ch);
	if (ch->eof) {
		if (n_ready == 0) {
			/* done */
			if (ch->saved_errno)
				ndmta_mover_halt (sess,
					NDMP9_MOVER_HALT_CONNECT_ERROR);
			else
				ndmta_mover_halt (sess,
					NDMP9_MOVER_HALT_CONNECT_CLOSED);

			did_something++;

			return did_something;
		}

		if (n_ready < count) {
			int		n_pad = count - n_ready;
			unsigned	n_avail;


			while (n_pad > 0) {
				n_avail = ndmchan_n_avail (ch);
				if (n_avail == 0) {
					/* Uh-oh */
				}
				data = &ch->data[ch->end_ix];
				if (n_avail > n_pad)
					n_avail = n_pad;
				bzero (data, n_avail);
				ch->end_ix += n_avail;
				n_pad -= n_avail;
			}
			n_ready = ndmchan_n_ready (ch);
		}
	}

	if (n_ready < count) {
		return did_something;	/* blocked */
	}

	if (ta->mover_want_pos >= ta->mover_window_end) {
		ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_SEEK);
		did_something++;
		return did_something;
	}

	data = &ch->data[ch->beg_ix];
	done_count = 0;

	error = ndmos_tape_write (sess, data, count, &done_count);

	switch (error) {
	case NDMP9_NO_ERR:
		if (done_count != count) {
			/* This ain't suppose to happen */
		}
		ta->mover_state.bytes_moved += count;
		ta->mover_want_pos += count;
		ch->beg_ix += count;
		did_something++;
		goto again;	/* write as much to tape as possible */

	case NDMP9_EOM_ERR:
		ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_EOM);
		did_something++;
		break;

	default:
		ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_MEDIA_ERROR);
		did_something++;
		break;
	}

	return did_something;
}
Пример #7
0
int
ndmda_quantum_image (struct ndm_session *sess)
{
	struct ndm_data_agent *	da = sess->data_acb;
	struct ndmchan *	from_chan;
	struct ndmchan *	to_chan;
	unsigned		n_ready, n_avail, n_copy;
	int			is_backup = 0;

	switch (da->data_state.operation) {
	default:
		assert (0);
		from_chan = 0;
		to_chan = 0;
		break;

	case NDMP9_DATA_OP_BACKUP:
		from_chan = &da->formatter_image;
		to_chan = &sess->plumb.image_stream->chan;
		is_backup = 1;
		break;

	case NDMP9_DATA_OP_RECOVER:
	case NDMP9_DATA_OP_RECOVER_FILEHIST:
		from_chan = &sess->plumb.image_stream->chan;
		to_chan = &da->formatter_image;
		break;
	}

  again:
	n_copy = n_ready = ndmchan_n_ready (from_chan);
	if (n_ready == 0) {
		if (from_chan->eof) {
			to_chan->eof = 1;
			if (ndmchan_n_ready (to_chan) == 0) {
				if (is_backup) {
					ndmda_data_halt (sess,
						NDMP9_DATA_HALT_SUCCESSFUL);
				}
			}
		}
		return 0;	/* data blocked */
	}

	n_avail = ndmchan_n_avail (to_chan);
	if (n_copy > n_avail)
		n_copy = n_avail;

	if (da->enable_hist) {
		if (n_copy > da->pass_resid)
			n_copy = da->pass_resid;
	}

	if (n_copy > 0) {
		bcopy (&from_chan->data[from_chan->beg_ix],
			&to_chan->data[to_chan->end_ix],
			n_copy);
		from_chan->beg_ix += n_copy;
		to_chan->end_ix += n_copy;
		da->data_state.bytes_processed += n_copy;
		da->pass_resid -= n_copy;
		goto again;	/* do as much as possible */
	}

	return 0;

}
Пример #8
0
int
ndmda_quantum_wrap (struct ndm_session *sess)
{
	struct ndm_data_agent *	da = sess->data_acb;
	struct ndmchan *	ch = &da->formatter_wrap;
	int			did_something = 0;
	char *			p;
	char *			data;
	char *			pend;
	unsigned		n_ready;
	int			is_recover = 0;

	switch (da->data_state.operation) {
	default:
		assert (0);
		break;

	case NDMP9_DATA_OP_BACKUP:
		break;

	case NDMP9_DATA_OP_RECOVER:
	case NDMP9_DATA_OP_RECOVER_FILEHIST:
		is_recover = 1;
		break;
	}

  again:
	n_ready = ndmchan_n_ready (ch);
	if (n_ready == 0) {
		if (ch->eof && is_recover) {
			ndmda_data_halt (sess, NDMP9_DATA_HALT_SUCCESSFUL);
		}
		return did_something;
	}
	data = p = &ch->data[ch->beg_ix];
	pend = p + n_ready;

	while (p < pend && *p != '\n') p++;


	if (p < pend && *p == '\n') {
		*p++ = 0;
		ndmda_wrap_in (sess, data);
		ch->beg_ix += p - data;
		did_something++;
		goto again;
	}

	if (!ch->eof)
		return did_something;

	/* content w/o newline, and EOF */
	/* p == pend */
	if (ch->end_ix >= ch->data_size) {
		if (data != ch->data) {
			ndmchan_compress (ch);
			goto again;
		}
		/* that's one huge message */
		p--;	/* lose last byte */
	}

	ch->data[ch->end_ix++] = '\n';
	did_something++;
	goto again;
}
Пример #9
0
int
ndmta_write_quantum (struct ndm_session *sess)
{
	struct ndm_tape_agent *	ta = sess->tape_acb;
	struct ndmchan *	ch = &sess->plumb.image_stream->chan;
	uint32_t		count = ta->mover_state.record_size;
	int			did_something = 0;
	uint64_t		max_read;
	uint64_t		want_window_off;
	uint32_t		block_size;
	uint32_t		want_blockno;
	uint32_t		cur_blockno;
	unsigned		n_avail, n_read, record_off;
	char *			data;
	uint32_t		done_count = 0;
	ndmp9_error		error;

  again:
	n_read = n_avail = ndmchan_n_avail_record (ch, count);
	if (n_avail < count) {
		/* allow to drain */
		return did_something;
	}

	if (ta->pending_change_after_drain) {
		if (ndmchan_n_ready (ch) > 0) {
			/* allow to drain */
		} else {
			ndmta_mover_apply_pending (sess);
			did_something++;
		}
		return did_something;
	}

	if (n_read > ta->mover_state.bytes_left_to_read)
		n_read = ta->mover_state.bytes_left_to_read;

	if (n_read < count) {
		/* Active, but paused awaiting MOVER_READ request */
		return did_something;	/* mover blocked */
	}

	if (ta->mover_want_pos < ta->mover_state.window_offset
	 || ta->mover_want_pos >= ta->mover_window_end) {
		ndmta_mover_pause_pending (sess, NDMP9_MOVER_PAUSE_SEEK);
		goto again;
	}

	max_read = ta->mover_window_end - ta->mover_want_pos;
	if (n_read > max_read)
		n_read = max_read;

	want_window_off = ta->mover_want_pos - ta->mover_state.window_offset;

	/* make an estimate of the block size - the tape agent's block size, or
	 * if it's in variable block size mode, the mover's record size: "When
	 * in variable block mode, as indicated by a tape block_size value of
	 * zero, the mover record size defines the actual block size used by
	 * the tape subsystem." (NDMPv4 RFC, Section 3.6.2.1) */
	block_size = ta->tape_state.block_size.value;
	if (!block_size)
		block_size = ta->mover_state.record_size;

	want_blockno = ta->mover_window_first_blockno + want_window_off / block_size;

	if (ta->tb_blockno != want_blockno) {
		uint32_t	xsr_count, xsr_resid;

		ndmos_tape_sync_state(sess);
		cur_blockno = ta->tape_state.blockno.value;
		if (cur_blockno < want_blockno) {
			xsr_count = want_blockno - cur_blockno;
			error = ndmos_tape_mtio (sess, NDMP9_MTIO_FSR,
						xsr_count, &xsr_resid);
			if (error == NDMP9_EOF_ERR) {
				ndmta_mover_pause_pending (sess,
						NDMP9_MOVER_PAUSE_EOF);
				goto again;
			}
			if (error != NDMP9_NO_ERR) {
				ndmta_mover_halt_pending (sess,
						NDMP9_MOVER_HALT_MEDIA_ERROR);
				goto again;
			}
			if (xsr_resid > 0) {
				ndmta_mover_pause_pending (sess,
						NDMP9_MOVER_PAUSE_EOF);
				goto again;
			}
		} else if (cur_blockno > want_blockno) {
			xsr_count = cur_blockno - want_blockno;
			error = ndmos_tape_mtio (sess, NDMP9_MTIO_BSR,
						xsr_count, &xsr_resid);
			if (error != NDMP9_NO_ERR || xsr_resid > 0) {
				ndmta_mover_halt_pending (sess,
						NDMP9_MOVER_HALT_MEDIA_ERROR);
				goto again;
			}
		} else {
			/* in position */
		}

		/*
		 * We are about to read data into a tape buffer so make sure
		 * we have it available. We delay allocating buffers to the
		 * moment we first need them.
		 */
		if (!ta->tape_buffer) {
			ta->tape_buffer = NDMOS_API_MALLOC (NDMOS_CONST_TAPE_REC_MAX);
			if (!ta->tape_buffer) {
				ndmta_mover_pause_pending (sess,
							NDMP9_MOVER_HALT_NA);
				goto again;
			}
		}

		data = ta->tape_buffer;
		done_count = 0;
		error = ndmos_tape_read (sess, data, count, &done_count);
		did_something++;

		if (error == NDMP9_EOF_ERR) {
			ndmta_mover_pause_pending (sess,
						NDMP9_MOVER_PAUSE_EOF);
			goto again;
		}
		/* N.B. - handling of done_count = 0 here is hacked to support
		 * non-blocking writes to a socket in amndmjob */
		if (error != NDMP9_NO_ERR) {
			ndmta_mover_halt_pending (sess,
				NDMP9_MOVER_HALT_MEDIA_ERROR);
			goto again;
		}
		if (done_count == 0) {
			return did_something - 1;
		}
		if (done_count != count) {
			goto again;
		}
		ta->tb_blockno = want_blockno;
		/* re-calcluate this, since record_size may be > block_size, in which
		 * case the record_num may not change for each block read from tape */
		ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
	}

	record_off = ta->mover_want_pos % ta->mover_state.record_size;

	n_avail = ta->mover_state.record_size - record_off;
	if (n_read > n_avail)
		n_read = n_avail;
	if (n_read != done_count) {
		printf("lost %lu bytes %lu %u\n", done_count - n_read, done_count, n_read);
		n_read = done_count;
	}

	/*
	 * We are about to read data into a tape buffer so make sure
	 * we have it available. We delay allocating buffers to the
	 * moment we first need them.
	 */
	if (!ta->tape_buffer) {
		ta->tape_buffer = NDMOS_API_MALLOC (NDMOS_CONST_TAPE_REC_MAX);
		if (!ta->tape_buffer) {
			ndmta_mover_pause_pending (sess,
						NDMP9_MOVER_HALT_NA);
			goto again;
		}
	}

	data = &ta->tape_buffer[record_off];

	bcopy (data, ch->data + ch->end_ix, n_read);
	ch->end_ix += n_read;
	ta->mover_state.bytes_moved += n_read;
	ta->mover_want_pos += n_read;
	ta->mover_state.bytes_left_to_read -= n_read;

	did_something++;

	goto again;	/* do as much as possible */
}
Пример #10
0
int
ndmchan_post_poll (struct ndmchan *chtab[], unsigned n_chtab)
{
	struct ndmchan *	ch;
	unsigned int		i;
	int			rc, len, n_ready;

	n_ready = 0;

	for (i = 0; i < n_chtab; i++) {
		ch = chtab[i];

		if (!ch->ready)
			continue;

		switch (ch->mode) {
		case NDMCHAN_MODE_READ:
			len = ndmchan_n_avail (ch);
			if (len <= 0) continue;

			n_ready++;
			rc = read (ch->fd, &ch->data[ch->end_ix], len);
			if (rc < 0) {
				if (errno != NDMOS_CONST_EWOULDBLOCK) {
					ch->error = ch->eof = 1;
					ch->saved_errno = errno;
					if (!ch->saved_errno)
						ch->saved_errno = -1;
				} else {
					/* no bytes read */
				}
			} else if (rc == 0) {
				ch->eof = 1;
				ch->error = 0;
				ch->saved_errno = 0;
			} else {
				ch->end_ix += rc;
			}
			break;

		case NDMCHAN_MODE_WRITE:
			len = ndmchan_n_ready (ch);
			if (len <= 0) continue;

			n_ready++;
			rc = write (ch->fd, &ch->data[ch->beg_ix], len);
			if (rc < 0) {
				if (errno != NDMOS_CONST_EWOULDBLOCK) {
					ch->eof = 1;
					ch->error = 1;
					ch->saved_errno = errno;
					if (!ch->saved_errno)
						ch->saved_errno = -1;
				} else {
					/* no bytes written */
					/* EWOULDBLOCK but ready? */
				}
			} else if (rc == 0) {
				/* NDMOS_CONST_EWOULDBLOCK? */
				ch->eof = 1;
				ch->error = 1;
				ch->saved_errno = 0;
			} else {
				ch->beg_ix += rc;
			}
			break;
		}
	}

	return n_ready;
}
Пример #11
0
int
ndmta_write_quantum (struct ndm_session *sess)
{
    struct ndm_tape_agent *	ta = &sess->tape_acb;
    struct ndmchan *	ch = &sess->plumb.image_stream.chan;
    unsigned long		count = ta->mover_state.record_size;
    int			did_something = 0;
    unsigned long long	max_read;
    unsigned long long	want_window_off;
    unsigned long		block_size;
    unsigned long		want_blockno;
    unsigned long		cur_blockno;
    unsigned		n_avail, n_read, record_off;
    char *			data;
    unsigned long		done_count;
    ndmp9_error		error;

again:
    n_read = n_avail = ndmchan_n_avail (ch);
    if (n_avail == 0) {
        /* allow to drain */
        return did_something;
    }

    if (ta->pending_change_after_drain) {
        if (ndmchan_n_ready (ch) > 0) {
            /* allow to drain */
        } else {
            ndmta_mover_apply_pending (sess);
            did_something++;
        }
        return did_something;
    }

    if (n_read > ta->mover_state.bytes_left_to_read)
        n_read = ta->mover_state.bytes_left_to_read;

    if (n_read == 0) {
        /* Active, but paused awaiting MOVER_READ request */
        return did_something;	/* mover blocked */
    }

    if (ta->mover_want_pos < ta->mover_state.window_offset
            || ta->mover_want_pos >= ta->mover_window_end) {
        ndmta_mover_pause_pending (sess, NDMP9_MOVER_PAUSE_SEEK);
        goto again;
    }

    max_read = ta->mover_window_end - ta->mover_want_pos;
    if (n_read > max_read)
        n_read = max_read;

    want_window_off = ta->mover_want_pos - ta->mover_state.window_offset;

    /* make an estimate of the block size - the tape agent's block size, or
     * if it's in variable block size mode, the mover's record size: "When
     * in variable block mode, as indicated by a tape block_size value of
     * zero, the mover record size defines the actual block size used by
     * the tape subsystem." (NDMPv4 RFC, Section 3.6.2.1) */
    block_size = ta->tape_state.block_size.value;
    if (!block_size)
        block_size = ta->mover_state.record_size;

    want_blockno = ta->mover_window_first_blockno + want_window_off / block_size;

    if (ta->tb_blockno != want_blockno) {
        unsigned long	xsr_count, xsr_resid;

        ndmos_tape_sync_state(sess);
        cur_blockno = ta->tape_state.blockno.value;
        if (cur_blockno < want_blockno) {
            xsr_count = want_blockno - cur_blockno;
            error = ndmos_tape_mtio (sess, NDMP9_MTIO_FSR,
                                     xsr_count, &xsr_resid);
            if (error == NDMP9_EOF_ERR) {
                ndmta_mover_pause_pending (sess,
                                           NDMP9_MOVER_PAUSE_EOF);
                goto again;
            }
            if (error != NDMP9_NO_ERR) {
                ndmta_mover_halt_pending (sess,
                                          NDMP9_MOVER_HALT_MEDIA_ERROR);
                goto again;
            }
            if (xsr_resid > 0) {
                ndmta_mover_pause_pending (sess,
                                           NDMP9_MOVER_PAUSE_EOF);
                goto again;
            }
        } else if (cur_blockno > want_blockno) {
            xsr_count = cur_blockno - want_blockno;
            error = ndmos_tape_mtio (sess, NDMP9_MTIO_BSR,
                                     xsr_count, &xsr_resid);
            if (error != NDMP9_NO_ERR || xsr_resid > 0) {
                ndmta_mover_halt_pending (sess,
                                          NDMP9_MOVER_HALT_MEDIA_ERROR);
                goto again;
            }
        } else {
            /* in position */
        }

        data = ta->tape_buffer;
        done_count = 0;
        error = ndmos_tape_read (sess, data, count, &done_count);
        did_something++;

        if (error == NDMP9_EOF_ERR) {
            ndmta_mover_pause_pending (sess,
                                       NDMP9_MOVER_PAUSE_EOF);
            goto again;
        }
        if (error != NDMP9_NO_ERR || done_count != count) {
            ndmta_mover_halt_pending (sess,
                                      NDMP9_MOVER_HALT_MEDIA_ERROR);
            goto again;
        }
        ta->tb_blockno = want_blockno;
        /* re-calcluate this, since record_size may be > block_size, in which
         * case the record_num may not change for each block read from tape */
        ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
    }

    record_off = ta->mover_want_pos % ta->mover_state.record_size;

    n_avail = ta->mover_state.record_size - record_off;
    if (n_read > n_avail)
        n_read = n_avail;

    data = &ta->tape_buffer[record_off];

    bcopy (data, ch->data + ch->end_ix, n_read);
    ch->end_ix += n_read;
    ta->mover_state.bytes_moved += n_read;
    ta->mover_want_pos += n_read;
    ta->mover_state.bytes_left_to_read -= n_read;

    did_something++;

    goto again;	/* do as much as possible */
}