1 /*
2  * Copyright (c) 1998,1999,2000
3  *	Traakan, Inc., Los Altos, CA
4  *	All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * Project:  NDMJOB
31  * Ident:    $Id: $
32  *
33  * Description:
34  *
35  */
36 
37 
38 #include "ndmagents.h"
39 
40 
41 #ifndef NDMOS_OPTION_NO_TAPE_AGENT
42 
43 
44 /*
45  * Initialization and Cleanup
46  ****************************************************************
47  */
48 
49 /* Initialize -- Set data structure to know value, ignore current value */
50 int
ndmta_initialize(struct ndm_session * sess)51 ndmta_initialize (struct ndm_session *sess)
52 {
53 	int			rc;
54 
55 	sess->tape_acb = NDMOS_API_MALLOC (sizeof(struct ndm_tape_agent));
56 	if (!sess->tape_acb)
57 		return -1;
58 	NDMOS_MACRO_ZEROFILL (sess->tape_acb);
59 
60 	ndmta_commission (sess);
61 
62 	rc = ndmos_tape_initialize (sess);
63 	if (rc) return rc;
64 
65 	return 0;
66 }
67 
68 /* Commission -- Get agent ready. Entire session has been initialize()d */
69 int
ndmta_commission(struct ndm_session * sess)70 ndmta_commission (struct ndm_session *sess)
71 {
72 	ndmta_init_mover_state (sess);
73 	return 0;
74 }
75 
76 /* Decommission -- Discard agent */
77 int
ndmta_decommission(struct ndm_session * sess)78 ndmta_decommission (struct ndm_session *sess)
79 {
80 	if (!sess->tape_acb)
81 		return 0;
82 
83 	ndmis_tape_close (sess);
84 
85 	return 0;
86 }
87 
88 /* Destroy -- Destroy agent */
89 int
ndmta_destroy(struct ndm_session * sess)90 ndmta_destroy (struct ndm_session *sess)
91 {
92 	if (!sess->tape_acb)
93 		return 0;
94 
95 	if (sess->tape_acb->tape_buffer) {
96 		NDMOS_API_FREE (sess->tape_acb->tape_buffer);
97 	}
98 
99 #ifdef NDMOS_OPTION_TAPE_SIMULATOR
100 	if (sess->tape_acb->drive_name) {
101 		NDMOS_API_FREE (sess->tape_acb->drive_name);
102 	}
103 #endif
104 
105 	NDMOS_API_FREE (sess->tape_acb);
106 	sess->tape_acb = NULL;
107 
108 	return 0;
109 }
110 
111 /* helper for mover state */
112 int
ndmta_init_mover_state(struct ndm_session * sess)113 ndmta_init_mover_state (struct ndm_session *sess)
114 {
115 	struct ndm_tape_agent *	ta = sess->tape_acb;
116 
117 	NDMOS_MACRO_ZEROFILL (&ta->mover_state);
118 
119 	ta->mover_state.state = NDMP9_MOVER_STATE_IDLE;
120 	ta->mover_state.window_offset = 0;
121 	ta->mover_state.record_num = 0; /* this should probably be -1, but spec says 0 */
122 	ta->mover_state.record_size = 20*512;	/* traditional tar default */
123 	ta->mover_state.window_length = NDMP_LENGTH_INFINITY;
124 	ta->mover_window_end = NDMP_LENGTH_INFINITY;
125 	ta->mover_want_pos = 0;
126 
127 	ta->tb_blockno = -1;
128 
129 	return 0;
130 }
131 
132 
133 
134 /*
135  * Semantic actions -- called from ndma_dispatch()
136  ****************************************************************
137  */
138 
139 void
ndmta_mover_sync_state(struct ndm_session * sess)140 ndmta_mover_sync_state (struct ndm_session *sess)
141 {
142 	ndmos_tape_sync_state (sess);
143 }
144 
145 ndmp9_error
ndmta_mover_listen(struct ndm_session * sess,ndmp9_mover_mode mover_mode)146 ndmta_mover_listen (struct ndm_session *sess, ndmp9_mover_mode mover_mode)
147 {
148 	struct ndm_tape_agent *	ta = sess->tape_acb;
149 
150 	ta->mover_state.mode = mover_mode;
151 
152 	ta->mover_state.state = NDMP9_MOVER_STATE_LISTEN;
153 	ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
154 	ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
155 
156 	return NDMP9_NO_ERR;
157 }
158 
159 ndmp9_error
ndmta_mover_connect(struct ndm_session * sess,ndmp9_mover_mode mover_mode)160 ndmta_mover_connect (struct ndm_session *sess, ndmp9_mover_mode mover_mode)
161 {
162 	struct ndm_tape_agent *	ta = sess->tape_acb;
163 
164 	ta->mover_state.mode = mover_mode;
165 
166 	ndmta_mover_start_active (sess);
167 
168 	return NDMP9_NO_ERR;
169 }
170 
171 void
ndmta_mover_halt(struct ndm_session * sess,ndmp9_mover_halt_reason reason)172 ndmta_mover_halt (struct ndm_session *sess, ndmp9_mover_halt_reason reason)
173 {
174 	struct ndm_tape_agent *	ta = sess->tape_acb;
175 
176 	ta->mover_state.state = NDMP9_MOVER_STATE_HALTED;
177 	ta->mover_state.halt_reason = reason;
178 	ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
179 	ta->pending_change_after_drain = 0;
180 	ta->mover_notify_pending = 1;
181 
182 	ndmis_tape_close (sess);
183 }
184 
185 void
ndmta_mover_pause(struct ndm_session * sess,ndmp9_mover_pause_reason reason)186 ndmta_mover_pause (struct ndm_session *sess, ndmp9_mover_pause_reason reason)
187 {
188 	struct ndm_tape_agent *	ta = sess->tape_acb;
189 
190 	ta->mover_state.state = NDMP9_MOVER_STATE_PAUSED;
191 	ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
192 	ta->mover_state.pause_reason = reason;
193 	ta->pending_change_after_drain = 0;
194 	ta->mover_notify_pending = 1;
195 }
196 
197 void
ndmta_mover_pending(struct ndm_session * sess,ndmp9_mover_state pending_state,ndmp9_mover_halt_reason halt_reason,ndmp9_mover_pause_reason pause_reason)198 ndmta_mover_pending (struct ndm_session *sess,
199   ndmp9_mover_state pending_state,
200   ndmp9_mover_halt_reason halt_reason,
201   ndmp9_mover_pause_reason pause_reason)
202 {
203 	struct ndm_tape_agent *	ta = sess->tape_acb;
204 
205 	if (ta->pending_change_after_drain) {
206 		/* internal botch */
207 	}
208 
209 	ta->pending_mover_state = pending_state;
210 	ta->pending_mover_halt_reason = halt_reason;
211 	ta->pending_mover_pause_reason = pause_reason;
212 	ta->pending_change_after_drain = 1;
213 }
214 
215 void
ndmta_mover_apply_pending(struct ndm_session * sess)216 ndmta_mover_apply_pending (struct ndm_session *sess)
217 {
218 	struct ndm_tape_agent *	ta = sess->tape_acb;
219 
220 	if (!ta->pending_change_after_drain) {
221 		/* internal botch */
222 	}
223 
224 	ta->mover_state.state = ta->pending_mover_state;
225 	ta->mover_state.halt_reason = ta->pending_mover_halt_reason;
226 	ta->mover_state.pause_reason = ta->pending_mover_pause_reason;
227 	ta->pending_change_after_drain = 0;
228 	ta->mover_notify_pending = 1;
229 }
230 
231 void
ndmta_mover_halt_pending(struct ndm_session * sess,ndmp9_mover_halt_reason halt_reason)232 ndmta_mover_halt_pending (struct ndm_session *sess,
233   ndmp9_mover_halt_reason halt_reason)
234 {
235 	ndmta_mover_pending (sess, NDMP9_MOVER_STATE_HALTED,
236 		halt_reason, NDMP9_MOVER_PAUSE_NA);
237 }
238 
239 void
ndmta_mover_pause_pending(struct ndm_session * sess,ndmp9_mover_pause_reason pause_reason)240 ndmta_mover_pause_pending (struct ndm_session *sess,
241   ndmp9_mover_pause_reason pause_reason)
242 {
243 	ndmta_mover_pending (sess, NDMP9_MOVER_STATE_PAUSED,
244 		NDMP9_MOVER_HALT_NA, pause_reason);
245 }
246 
247 void
ndmta_mover_active(struct ndm_session * sess)248 ndmta_mover_active (struct ndm_session *sess)
249 {
250 	struct ndm_tape_agent *	ta = sess->tape_acb;
251 
252 	ta->mover_state.state = NDMP9_MOVER_STATE_ACTIVE;
253 	ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
254 	ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
255 
256 	ta->tb_blockno = -1;	/* always mistrust after activating */
257 }
258 
259 void
ndmta_mover_start_active(struct ndm_session * sess)260 ndmta_mover_start_active (struct ndm_session *sess)
261 {
262 	struct ndm_tape_agent *	ta = sess->tape_acb;
263 
264 	ndmalogf (sess, 0, 6, "mover going active");
265 	ndma_send_logmsg(sess, NDMP9_LOG_DEBUG, sess->plumb.control,
266 		"mover going active");
267 
268 	switch (ta->mover_state.mode) {
269 	case NDMP9_MOVER_MODE_READ:
270 		ndmis_tape_start (sess, NDMCHAN_MODE_READ);
271 		ndmta_mover_active (sess);
272 		break;
273 
274 	case NDMP9_MOVER_MODE_WRITE:
275 		ndmis_tape_start (sess, NDMCHAN_MODE_WRITE);
276 		ndmta_mover_active (sess);
277 		break;
278 
279 	default:
280 		ndmalogf (sess, 0, 0, "BOTCH mover listen, unknown mode");
281 		break;
282 	}
283 }
284 
285 void
ndmta_mover_stop(struct ndm_session * sess)286 ndmta_mover_stop (struct ndm_session *sess)
287 {
288 	ndmta_init_mover_state (sess);
289 }
290 
291 void
ndmta_mover_abort(struct ndm_session * sess)292 ndmta_mover_abort (struct ndm_session *sess)
293 {
294 	ndmta_mover_halt (sess, NDMP9_MOVER_HALT_ABORTED);
295 }
296 
297 void
ndmta_mover_continue(struct ndm_session * sess)298 ndmta_mover_continue (struct ndm_session *sess)
299 {
300 	ndmta_mover_active (sess);
301 }
302 
303 void
ndmta_mover_close(struct ndm_session * sess)304 ndmta_mover_close (struct ndm_session *sess)
305 {
306 	struct ndm_tape_agent *	ta = sess->tape_acb;
307 
308 	if (ta->mover_state.state != NDMP9_MOVER_STATE_HALTED)
309 		ndmta_mover_halt (sess, NDMP9_MOVER_HALT_CONNECT_CLOSED);
310 }
311 
312 void
ndmta_mover_read(struct ndm_session * sess,uint64_t offset,uint64_t length)313 ndmta_mover_read (struct ndm_session *sess,
314   uint64_t offset, uint64_t length)
315 {
316 	struct ndm_tape_agent *	ta = sess->tape_acb;
317 
318 	ta->mover_state.seek_position = offset;
319 	ta->mover_state.bytes_left_to_read = length;
320 	ta->mover_want_pos = offset;
321 }
322 
323 
324 
325 /*
326  * Quantum -- get a bit of work done
327  ****************************************************************
328  */
329 
330 int
ndmta_quantum(struct ndm_session * sess)331 ndmta_quantum (struct ndm_session *sess)
332 {
333 	struct ndm_tape_agent *	ta = sess->tape_acb;
334 	int			rc = 0;	/* did nothing */
335 
336 	switch (ta->mover_state.state) {
337 	default:
338 		ndmalogf (sess, 0, 0, "BOTCH mover state");
339 		return -1;
340 
341 	case NDMP9_MOVER_STATE_IDLE:
342 	case NDMP9_MOVER_STATE_PAUSED:
343 	case NDMP9_MOVER_STATE_HALTED:
344 		break;
345 
346 	case NDMP9_MOVER_STATE_LISTEN:
347 		switch (sess->plumb.image_stream->tape_ep.connect_status) {
348 		case NDMIS_CONN_LISTEN:		/* no connection yet */
349 			break;
350 
351 		case NDMIS_CONN_ACCEPTED:	/* we're in business */
352 			ndmta_mover_start_active (sess);
353 			rc = 1;		/* did something */
354 			break;
355 
356 		case NDMIS_CONN_BOTCHED:	/* accept() went south */
357 		default:			/* ain't suppose to happen */
358 			ndmta_mover_halt(sess,NDMP9_MOVER_HALT_CONNECT_ERROR);
359 			break;
360 		}
361 		break;
362 
363 	case NDMP9_MOVER_STATE_ACTIVE:
364 		switch (ta->mover_state.mode) {
365 		case NDMP9_MOVER_MODE_READ:
366 			rc = ndmta_read_quantum (sess);
367 			break;
368 
369 		case NDMP9_MOVER_MODE_WRITE:
370 			rc = ndmta_write_quantum (sess);
371 			break;
372 
373 		default:
374 			ndmalogf (sess, 0, 0,
375 				"BOTCH mover active, unknown mode");
376 			return -1;
377 		}
378 		break;
379 	}
380 
381 	ndmta_mover_send_notice (sess);
382 
383 	return rc;
384 }
385 
386 int
ndmta_read_quantum(struct ndm_session * sess)387 ndmta_read_quantum (struct ndm_session *sess)
388 {
389 	struct ndm_tape_agent *	ta = sess->tape_acb;
390 	struct ndmchan *	ch = &sess->plumb.image_stream->chan;
391 	uint32_t		count = ta->mover_state.record_size;
392 	int			did_something = 0;
393 	unsigned		n_ready;
394 	char *			data;
395 	uint32_t		done_count;
396 	ndmp9_error		error;
397 
398   again:
399 	n_ready = ndmchan_n_ready (ch);
400 	if (ch->eof) {
401 		if (n_ready == 0) {
402 			/* done */
403 			if (ch->saved_errno)
404 				ndmta_mover_halt (sess,
405 					NDMP9_MOVER_HALT_CONNECT_ERROR);
406 			else
407 				ndmta_mover_halt (sess,
408 					NDMP9_MOVER_HALT_CONNECT_CLOSED);
409 
410 			did_something++;
411 
412 			return did_something;
413 		}
414 
415 		if (n_ready < count) {
416 			int		n_pad = count - n_ready;
417 			int		n_avail;
418 
419 
420 			while (n_pad > 0) {
421 				n_avail = ndmchan_n_avail (ch);
422 				if (n_avail == 0) {
423 					/* Uh-oh */
424 				}
425 				data = &ch->data[ch->end_ix];
426 				if (n_avail > n_pad)
427 					n_avail = n_pad;
428 				bzero (data, n_avail);
429 				ch->end_ix += n_avail;
430 				n_pad -= n_avail;
431 			}
432 			n_ready = ndmchan_n_ready (ch);
433 		}
434 	}
435 
436 	if (n_ready < count) {
437 		return did_something;	/* blocked */
438 	}
439 
440 	if (ta->mover_want_pos >= ta->mover_window_end) {
441 		ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_SEEK);
442 		did_something++;
443 		return did_something;
444 	}
445 
446 	data = &ch->data[ch->beg_ix];
447 	done_count = 0;
448 
449 	error = ndmos_tape_write (sess, data, count, &done_count);
450 
451 	switch (error) {
452 	case NDMP9_NO_ERR:
453 		if (done_count != count) {
454 			/* This ain't suppose to happen */
455 		}
456 		ta->mover_state.bytes_moved += count;
457 		ta->mover_want_pos += count;
458 		ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
459 		ch->beg_ix += count;
460 		did_something++;
461 		goto again;	/* write as much to tape as possible */
462 
463 	case NDMP9_EOM_ERR:
464 		ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_EOM);
465 		did_something++;
466 		break;
467 
468 	default:
469 		ndmta_mover_halt (sess, NDMP9_MOVER_HALT_MEDIA_ERROR);
470 		did_something++;
471 		break;
472 	}
473 
474 	return did_something;
475 }
476 
477 
478 int
ndmta_write_quantum(struct ndm_session * sess)479 ndmta_write_quantum (struct ndm_session *sess)
480 {
481 	struct ndm_tape_agent *	ta = sess->tape_acb;
482 	struct ndmchan *	ch = &sess->plumb.image_stream->chan;
483 	uint32_t		count = ta->mover_state.record_size;
484 	int			did_something = 0;
485 	uint64_t		max_read;
486 	uint64_t		want_window_off;
487 	uint32_t		block_size;
488 	uint32_t		want_blockno;
489 	uint32_t		cur_blockno;
490 	unsigned		n_avail, n_read, record_off;
491 	char *			data;
492 	uint32_t		done_count = 0;
493 	ndmp9_error		error;
494 
495   again:
496 	n_read = n_avail = ndmchan_n_avail_record (ch, count);
497 	if (n_avail < count) {
498 		/* allow to drain */
499 		return did_something;
500 	}
501 
502 	if (ta->pending_change_after_drain) {
503 		if (ndmchan_n_ready (ch) > 0) {
504 			/* allow to drain */
505 		} else {
506 			ndmta_mover_apply_pending (sess);
507 			did_something++;
508 		}
509 		return did_something;
510 	}
511 
512 	if (n_read > ta->mover_state.bytes_left_to_read)
513 		n_read = ta->mover_state.bytes_left_to_read;
514 
515 	if (n_read < count) {
516 		/* Active, but paused awaiting MOVER_READ request */
517 		return did_something;	/* mover blocked */
518 	}
519 
520 	if (ta->mover_want_pos < ta->mover_state.window_offset
521 	 || ta->mover_want_pos >= ta->mover_window_end) {
522 		ndmta_mover_pause_pending (sess, NDMP9_MOVER_PAUSE_SEEK);
523 		goto again;
524 	}
525 
526 	max_read = ta->mover_window_end - ta->mover_want_pos;
527 	if (n_read > max_read)
528 		n_read = max_read;
529 
530 	want_window_off = ta->mover_want_pos - ta->mover_state.window_offset;
531 
532 	/* make an estimate of the block size - the tape agent's block size, or
533 	 * if it's in variable block size mode, the mover's record size: "When
534 	 * in variable block mode, as indicated by a tape block_size value of
535 	 * zero, the mover record size defines the actual block size used by
536 	 * the tape subsystem." (NDMPv4 RFC, Section 3.6.2.1) */
537 	block_size = ta->tape_state.block_size.value;
538 	if (!block_size)
539 		block_size = ta->mover_state.record_size;
540 
541 	want_blockno = ta->mover_window_first_blockno + want_window_off / block_size;
542 
543 	if (ta->tb_blockno != want_blockno) {
544     uint32_t	xsr_count = 0;
545     uint32_t  xsr_resid = 0;
546 
547 		ndmos_tape_sync_state(sess);
548 		cur_blockno = ta->tape_state.blockno.value;
549 		if (cur_blockno < want_blockno) {
550 			xsr_count = want_blockno - cur_blockno;
551 	      ndmalogf (sess, 0, 6, "NDMP9_MTIO_FSR %lu", xsr_count);
552 			error = ndmos_tape_mtio (sess, NDMP9_MTIO_FSR,
553 						xsr_count, &xsr_resid);
554 			if (error == NDMP9_EOF_ERR) {
555 				ndmta_mover_pause_pending (sess,
556 						NDMP9_MOVER_PAUSE_EOF);
557 				goto again;
558 			}
559 			if (error != NDMP9_NO_ERR) {
560 				ndmta_mover_halt_pending (sess,
561 						NDMP9_MOVER_HALT_MEDIA_ERROR);
562 				goto again;
563 			}
564 			if (xsr_resid > 0) {
565 				ndmta_mover_pause_pending (sess,
566 						NDMP9_MOVER_PAUSE_EOF);
567 				goto again;
568 			}
569 		} else if (cur_blockno > want_blockno) {
570 	      ndmalogf (sess, 0, 6, "NDMP9_MTIO_BSR %lu", xsr_count);
571 			xsr_count = cur_blockno - want_blockno;
572 			error = ndmos_tape_mtio (sess, NDMP9_MTIO_BSR,
573 						xsr_count, &xsr_resid);
574 			if (error != NDMP9_NO_ERR || xsr_resid > 0) {
575 				ndmta_mover_halt_pending (sess,
576 						NDMP9_MOVER_HALT_MEDIA_ERROR);
577 				goto again;
578 			}
579 		} else {
580 			/* in position */
581 		}
582 
583 		/*
584 		 * We are about to read data into a tape buffer so make sure
585 		 * we have it available. We delay allocating buffers to the
586 		 * moment we first need them.
587 		 */
588 		if (!ta->tape_buffer) {
589 			ta->tape_buffer = NDMOS_API_MALLOC (NDMOS_CONST_TAPE_REC_MAX);
590 			if (!ta->tape_buffer) {
591 				ndmta_mover_pause_pending (sess,
592 							NDMP9_MOVER_HALT_NA);
593 				goto again;
594 			}
595 		}
596 
597 		data = ta->tape_buffer;
598 		done_count = 0;
599 		error = ndmos_tape_read (sess, data, count, &done_count);
600 		did_something++;
601 
602 		if (error == NDMP9_EOF_ERR) {
603 			ndmta_mover_pause_pending (sess,
604 						NDMP9_MOVER_PAUSE_EOF);
605 			goto again;
606 		}
607 		/* N.B. - handling of done_count = 0 here is hacked to support
608 		 * non-blocking writes to a socket in amndmjob */
609 		if (error != NDMP9_NO_ERR) {
610 			ndmta_mover_halt_pending (sess,
611 				NDMP9_MOVER_HALT_MEDIA_ERROR);
612 			goto again;
613 		}
614 		if (done_count == 0) {
615 			return did_something - 1;
616 		}
617 		if (done_count != count) {
618 			goto again;
619 		}
620 		ta->tb_blockno = want_blockno;
621 		/* re-calcluate this, since record_size may be > block_size, in which
622 		 * case the record_num may not change for each block read from tape */
623 		ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
624 	}
625 
626 	record_off = ta->mover_want_pos % ta->mover_state.record_size;
627 
628 	n_avail = ta->mover_state.record_size - record_off;
629 	if (n_read > n_avail)
630 		n_read = n_avail;
631 	if (n_read != done_count) {
632 		printf("lost %lu bytes %lu %u\n", done_count - n_read, done_count, n_read);
633 		n_read = done_count;
634 	}
635 
636 	/*
637 	 * We are about to read data into a tape buffer so make sure
638 	 * we have it available. We delay allocating buffers to the
639 	 * moment we first need them.
640 	 */
641 	if (!ta->tape_buffer) {
642 		ta->tape_buffer = NDMOS_API_MALLOC (NDMOS_CONST_TAPE_REC_MAX);
643 		if (!ta->tape_buffer) {
644 			ndmta_mover_pause_pending (sess,
645 						NDMP9_MOVER_HALT_NA);
646 			goto again;
647 		}
648 	}
649 
650 	data = &ta->tape_buffer[record_off];
651 
652 	bcopy (data, ch->data + ch->end_ix, n_read);
653 	ch->end_ix += n_read;
654 	ta->mover_state.bytes_moved += n_read;
655 	ta->mover_want_pos += n_read;
656 	ta->mover_state.bytes_left_to_read -= n_read;
657 
658 	did_something++;
659 
660 	goto again;	/* do as much as possible */
661 }
662 
663 void
ndmta_mover_send_notice(struct ndm_session * sess)664 ndmta_mover_send_notice (struct ndm_session *sess)
665 {
666 	struct ndm_tape_agent *	ta = sess->tape_acb;
667 
668 	if (!ta->mover_notify_pending)
669 		return;
670 
671 	ta->mover_notify_pending = 0;
672 
673 	switch (ta->mover_state.state) {
674 	case NDMP9_MOVER_STATE_HALTED:
675 		ndma_notify_mover_halted (sess);
676 		break;
677 
678 	case NDMP9_MOVER_STATE_PAUSED:
679 		ndma_notify_mover_paused (sess);
680 		break;
681 
682 	default:
683 		/* Hmm. Why are we here. Race? */
684 		break;
685 	}
686 }
687 
688 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */
689