Imported Upstream version 3.3.0
[debian/amanda] / ndmp-src / ndma_tape.c
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
51 ndmta_initialize (struct ndm_session *sess)
52 {
53         struct ndm_tape_agent * ta = &sess->tape_acb;
54         int                     rc;
55
56         NDMOS_MACRO_ZEROFILL(ta);
57
58         ndmta_commission (sess);
59
60         rc = ndmos_tape_initialize (sess);
61         if (rc) return rc;
62
63         return 0;
64 }
65
66 /* Commission -- Get agent ready. Entire session has been initialize()d */
67 int
68 ndmta_commission (struct ndm_session *sess)
69 {
70         ndmta_init_mover_state (sess);
71         return 0;
72 }
73
74 /* Decommission -- Discard agent */
75 int
76 ndmta_decommission (struct ndm_session *sess)
77 {
78         ndmis_tape_close (sess);
79         ndmta_commission (sess);
80
81         return 0;
82 }
83
84 /* helper for mover state */
85 int
86 ndmta_init_mover_state (struct ndm_session *sess)
87 {
88         struct ndm_tape_agent * ta = &sess->tape_acb;
89
90         NDMOS_MACRO_ZEROFILL (&ta->mover_state);
91
92         ta->mover_state.state = NDMP9_MOVER_STATE_IDLE;
93         ta->mover_state.window_offset = 0;
94         ta->mover_state.record_num = 0; /* this should probably be -1, but spec says 0 */
95         ta->mover_state.record_size = 20*512;   /* traditional tar default */
96         ta->mover_state.window_length = NDMP_LENGTH_INFINITY;
97         ta->mover_window_end = NDMP_LENGTH_INFINITY;
98         ta->mover_want_pos = 0;
99
100         ta->tb_blockno = -1;
101
102         return 0;
103 }
104
105
106
107 /*
108  * Semantic actions -- called from ndma_dispatch()
109  ****************************************************************
110  */
111
112 void
113 ndmta_mover_sync_state (struct ndm_session *sess)
114 {
115         ndmos_tape_sync_state (sess);
116 }
117
118 ndmp9_error
119 ndmta_mover_listen (struct ndm_session *sess, ndmp9_mover_mode mover_mode)
120 {
121         struct ndm_tape_agent * ta = &sess->tape_acb;
122
123         ta->mover_state.mode = mover_mode;
124
125         ta->mover_state.state = NDMP9_MOVER_STATE_LISTEN;
126         ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
127         ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
128
129         return NDMP9_NO_ERR;
130 }
131
132 ndmp9_error
133 ndmta_mover_connect (struct ndm_session *sess, ndmp9_mover_mode mover_mode)
134 {
135         struct ndm_tape_agent * ta = &sess->tape_acb;
136
137         ta->mover_state.mode = mover_mode;
138
139         ndmta_mover_start_active (sess);
140
141         return NDMP9_NO_ERR;
142 }
143
144 void
145 ndmta_mover_halt (struct ndm_session *sess, ndmp9_mover_halt_reason reason)
146 {
147         struct ndm_tape_agent * ta = &sess->tape_acb;
148
149         ta->mover_state.state = NDMP9_MOVER_STATE_HALTED;
150         ta->mover_state.halt_reason = reason;
151         ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
152         ta->pending_change_after_drain = 0;
153         ta->mover_notify_pending = 1;
154
155         ndmis_tape_close (sess);
156 }
157
158 void
159 ndmta_mover_pause (struct ndm_session *sess, ndmp9_mover_pause_reason reason)
160 {
161         struct ndm_tape_agent * ta = &sess->tape_acb;
162
163         ta->mover_state.state = NDMP9_MOVER_STATE_PAUSED;
164         ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
165         ta->mover_state.pause_reason = reason;
166         ta->pending_change_after_drain = 0;
167         ta->mover_notify_pending = 1;
168 }
169
170 void
171 ndmta_mover_pending (struct ndm_session *sess,
172   ndmp9_mover_state pending_state,
173   ndmp9_mover_halt_reason halt_reason,
174   ndmp9_mover_pause_reason pause_reason)
175 {
176         struct ndm_tape_agent * ta = &sess->tape_acb;
177
178         if (ta->pending_change_after_drain) {
179                 /* internal botch */
180         }
181
182         ta->pending_mover_state = pending_state;
183         ta->pending_mover_halt_reason = halt_reason;
184         ta->pending_mover_pause_reason = pause_reason;
185         ta->pending_change_after_drain = 1;
186 }
187
188 void
189 ndmta_mover_apply_pending (struct ndm_session *sess)
190 {
191         struct ndm_tape_agent * ta = &sess->tape_acb;
192
193         if (!ta->pending_change_after_drain) {
194                 /* internal botch */
195         }
196
197         ta->mover_state.state = ta->pending_mover_state;
198         ta->mover_state.halt_reason = ta->pending_mover_halt_reason;
199         ta->mover_state.pause_reason = ta->pending_mover_pause_reason;
200         ta->pending_change_after_drain = 0;
201         ta->mover_notify_pending = 1;
202 }
203
204 void
205 ndmta_mover_halt_pending (struct ndm_session *sess,
206   ndmp9_mover_halt_reason halt_reason)
207 {
208         ndmta_mover_pending (sess, NDMP9_MOVER_STATE_HALTED,
209                 halt_reason, NDMP9_MOVER_PAUSE_NA);
210 }
211
212 void
213 ndmta_mover_pause_pending (struct ndm_session *sess,
214   ndmp9_mover_pause_reason pause_reason)
215 {
216         ndmta_mover_pending (sess, NDMP9_MOVER_STATE_PAUSED,
217                 NDMP9_MOVER_HALT_NA, pause_reason);
218 }
219
220 void
221 ndmta_mover_active (struct ndm_session *sess)
222 {
223         struct ndm_tape_agent * ta = &sess->tape_acb;
224
225         ta->mover_state.state = NDMP9_MOVER_STATE_ACTIVE;
226         ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
227         ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
228
229         ta->tb_blockno = -1;    /* always mistrust after activating */
230 }
231
232 void
233 ndmta_mover_start_active (struct ndm_session *sess)
234 {
235         struct ndm_tape_agent * ta = &sess->tape_acb;
236
237         ndmalogf (sess, 0, 6, "mover going active");
238         ndma_send_logmsg(sess, NDMP9_LOG_DEBUG, sess->plumb.control,
239                 "mover going active");
240
241         switch (ta->mover_state.mode) {
242         case NDMP9_MOVER_MODE_READ:
243                 ndmis_tape_start (sess, NDMCHAN_MODE_READ);
244                 ndmta_mover_active (sess);
245                 break;
246
247         case NDMP9_MOVER_MODE_WRITE:
248                 ndmis_tape_start (sess, NDMCHAN_MODE_WRITE);
249                 ndmta_mover_active (sess);
250                 break;
251
252         default:
253                 ndmalogf (sess, 0, 0, "BOTCH mover listen, unknown mode");
254                 break;
255         }
256 }
257
258 void
259 ndmta_mover_stop (struct ndm_session *sess)
260 {
261         ndmta_init_mover_state (sess);
262 }
263
264 void
265 ndmta_mover_abort (struct ndm_session *sess)
266 {
267         ndmta_mover_halt (sess, NDMP9_MOVER_HALT_ABORTED);
268 }
269
270 void
271 ndmta_mover_continue (struct ndm_session *sess)
272 {
273         ndmta_mover_active (sess);
274 }
275
276 void
277 ndmta_mover_close (struct ndm_session *sess)
278 {
279         struct ndm_tape_agent * ta = &sess->tape_acb;
280
281         if (ta->mover_state.state != NDMP9_MOVER_STATE_HALTED)
282                 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_CONNECT_CLOSED);
283 }
284
285 void
286 ndmta_mover_read (struct ndm_session *sess,
287   unsigned long long offset, unsigned long long length)
288 {
289         struct ndm_tape_agent * ta = &sess->tape_acb;
290
291         ta->mover_state.seek_position = offset;
292         ta->mover_state.bytes_left_to_read = length;
293         ta->mover_want_pos = offset;
294 }
295
296
297
298 /*
299  * Quantum -- get a bit of work done
300  ****************************************************************
301  */
302
303 int
304 ndmta_quantum (struct ndm_session *sess)
305 {
306         struct ndm_tape_agent * ta = &sess->tape_acb;
307         int                     rc = 0; /* did nothing */
308
309         switch (ta->mover_state.state) {
310         default:
311                 ndmalogf (sess, 0, 0, "BOTCH mover state");
312                 return -1;
313
314         case NDMP9_MOVER_STATE_IDLE:
315         case NDMP9_MOVER_STATE_PAUSED:
316         case NDMP9_MOVER_STATE_HALTED:
317                 break;
318
319         case NDMP9_MOVER_STATE_LISTEN:
320                 switch (sess->plumb.image_stream.tape_ep.connect_status) {
321                 case NDMIS_CONN_LISTEN:         /* no connection yet */
322                         break;
323
324                 case NDMIS_CONN_ACCEPTED:       /* we're in business */
325                         ndmta_mover_start_active (sess);
326                         rc = 1;         /* did something */
327                         break;
328
329                 case NDMIS_CONN_BOTCHED:        /* accept() went south */
330                 default:                        /* ain't suppose to happen */
331                         ndmta_mover_halt(sess,NDMP9_MOVER_HALT_CONNECT_ERROR);
332                         break;
333                 }
334                 break;
335
336         case NDMP9_MOVER_STATE_ACTIVE:
337                 switch (ta->mover_state.mode) {
338                 case NDMP9_MOVER_MODE_READ:
339                         rc = ndmta_read_quantum (sess);
340                         break;
341
342                 case NDMP9_MOVER_MODE_WRITE:
343                         rc = ndmta_write_quantum (sess);
344                         break;
345
346                 default:
347                         ndmalogf (sess, 0, 0,
348                                 "BOTCH mover active, unknown mode");
349                         return -1;
350                 }
351                 break;
352         }
353
354         ndmta_mover_send_notice (sess);
355
356         return rc;
357 }
358
359 int
360 ndmta_read_quantum (struct ndm_session *sess)
361 {
362         struct ndm_tape_agent * ta = &sess->tape_acb;
363         struct ndmchan *        ch = &sess->plumb.image_stream.chan;
364         unsigned long           count = ta->mover_state.record_size;
365         int                     did_something = 0;
366         unsigned                n_ready;
367         char *                  data;
368         unsigned long           done_count;
369         ndmp9_error             error;
370
371   again:
372         n_ready = ndmchan_n_ready (ch);
373         if (ch->eof) {
374                 if (n_ready == 0) {
375                         /* done */
376                         if (ch->saved_errno)
377                                 ndmta_mover_halt (sess,
378                                         NDMP9_MOVER_HALT_CONNECT_ERROR);
379                         else
380                                 ndmta_mover_halt (sess,
381                                         NDMP9_MOVER_HALT_CONNECT_CLOSED);
382
383                         did_something++;
384
385                         return did_something;
386                 }
387
388                 if (n_ready < count) {
389                         int             n_pad = count - n_ready;
390                         int             n_avail;
391
392
393                         while (n_pad > 0) {
394                                 n_avail = ndmchan_n_avail (ch);
395                                 if (n_avail == 0) {
396                                         /* Uh-oh */
397                                 }
398                                 data = &ch->data[ch->end_ix];
399                                 if (n_avail > n_pad)
400                                         n_avail = n_pad;
401                                 bzero (data, n_avail);
402                                 ch->end_ix += n_avail;
403                                 n_pad -= n_avail;
404                         }
405                         n_ready = ndmchan_n_ready (ch);
406                 }
407         }
408
409         if (n_ready < count) {
410                 return did_something;   /* blocked */
411         }
412
413         if (ta->mover_want_pos >= ta->mover_window_end) {
414                 ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_SEEK);
415                 did_something++;
416                 return did_something;
417         }
418
419         data = &ch->data[ch->beg_ix];
420         done_count = 0;
421
422         error = ndmos_tape_write (sess, data, count, &done_count);
423
424         switch (error) {
425         case NDMP9_NO_ERR:
426                 if (done_count != count) {
427                         /* This ain't suppose to happen */
428                 }
429                 ta->mover_state.bytes_moved += count;
430                 /* note this is calculated before mover_want_pos is incremented, since
431                  * record_num is the *last* block processed */
432                 ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
433                 ta->mover_want_pos += count;
434                 ch->beg_ix += count;
435                 did_something++;
436                 goto again;     /* write as much to tape as possible */
437
438         case NDMP9_EOM_ERR:
439                 ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_EOM);
440                 did_something++;
441                 break;
442
443         default:
444                 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_MEDIA_ERROR);
445                 did_something++;
446                 break;
447         }
448
449         return did_something;
450 }
451
452
453 int
454 ndmta_write_quantum (struct ndm_session *sess)
455 {
456         struct ndm_tape_agent * ta = &sess->tape_acb;
457         struct ndmchan *        ch = &sess->plumb.image_stream.chan;
458         unsigned long           count = ta->mover_state.record_size;
459         int                     did_something = 0;
460         unsigned long long      max_read;
461         unsigned long long      want_window_off;
462         unsigned long           block_size;
463         unsigned long           want_blockno;
464         unsigned long           cur_blockno;
465         unsigned                n_avail, n_read, record_off;
466         char *                  data;
467         unsigned long           done_count;
468         ndmp9_error             error;
469
470   again:
471         n_read = n_avail = ndmchan_n_avail_record (ch, count);
472         if (n_avail < count) {
473                 /* allow to drain */
474                 return did_something;
475         }
476
477         if (ta->pending_change_after_drain) {
478                 if (ndmchan_n_ready (ch) > 0) {
479                         /* allow to drain */
480                 } else {
481                         ndmta_mover_apply_pending (sess);
482                         did_something++;
483                 }
484                 return did_something;
485         }
486
487         if (n_read > ta->mover_state.bytes_left_to_read)
488                 n_read = ta->mover_state.bytes_left_to_read;
489
490         if (n_read < count) {
491                 /* Active, but paused awaiting MOVER_READ request */
492                 return did_something;   /* mover blocked */
493         }
494
495         if (ta->mover_want_pos < ta->mover_state.window_offset
496          || ta->mover_want_pos >= ta->mover_window_end) {
497                 ndmta_mover_pause_pending (sess, NDMP9_MOVER_PAUSE_SEEK);
498                 goto again;
499         }
500
501         max_read = ta->mover_window_end - ta->mover_want_pos;
502         if (n_read > max_read)
503                 n_read = max_read;
504
505         want_window_off = ta->mover_want_pos - ta->mover_state.window_offset;
506
507         /* make an estimate of the block size - the tape agent's block size, or
508          * if it's in variable block size mode, the mover's record size: "When
509          * in variable block mode, as indicated by a tape block_size value of
510          * zero, the mover record size defines the actual block size used by
511          * the tape subsystem." (NDMPv4 RFC, Section 3.6.2.1) */
512         block_size = ta->tape_state.block_size.value;
513         if (!block_size)
514                 block_size = ta->mover_state.record_size;
515
516         want_blockno = ta->mover_window_first_blockno + want_window_off / block_size;
517
518         if (ta->tb_blockno != want_blockno) {
519                 unsigned long   xsr_count, xsr_resid;
520
521                 ndmos_tape_sync_state(sess);
522                 cur_blockno = ta->tape_state.blockno.value;
523                 if (cur_blockno < want_blockno) {
524                         xsr_count = want_blockno - cur_blockno;
525                         error = ndmos_tape_mtio (sess, NDMP9_MTIO_FSR,
526                                                 xsr_count, &xsr_resid);
527                         if (error == NDMP9_EOF_ERR) {
528                                 ndmta_mover_pause_pending (sess,
529                                                 NDMP9_MOVER_PAUSE_EOF);
530                                 goto again;
531                         }
532                         if (error != NDMP9_NO_ERR) {
533                                 ndmta_mover_halt_pending (sess,
534                                                 NDMP9_MOVER_HALT_MEDIA_ERROR);
535                                 goto again;
536                         }
537                         if (xsr_resid > 0) {
538                                 ndmta_mover_pause_pending (sess,
539                                                 NDMP9_MOVER_PAUSE_EOF);
540                                 goto again;
541                         }
542                 } else if (cur_blockno > want_blockno) {
543                         xsr_count = cur_blockno - want_blockno;
544                         error = ndmos_tape_mtio (sess, NDMP9_MTIO_BSR,
545                                                 xsr_count, &xsr_resid);
546                         if (error != NDMP9_NO_ERR || xsr_resid > 0) {
547                                 ndmta_mover_halt_pending (sess,
548                                                 NDMP9_MOVER_HALT_MEDIA_ERROR);
549                                 goto again;
550                         }
551                 } else {
552                         /* in position */
553                 }
554
555                 data = ta->tape_buffer;
556                 done_count = 0;
557                 error = ndmos_tape_read (sess, data, count, &done_count);
558                 did_something++;
559
560                 if (error == NDMP9_EOF_ERR) {
561                         ndmta_mover_pause_pending (sess,
562                                                 NDMP9_MOVER_PAUSE_EOF);
563                         goto again;
564                 }
565                 /* N.B. - handling of done_count = 0 here is hacked to support
566                  * non-blocking writes to a socket in amndmjob */
567                 if (error != NDMP9_NO_ERR) {
568                         ndmta_mover_halt_pending (sess,
569                                 NDMP9_MOVER_HALT_MEDIA_ERROR);
570                         goto again;
571                 }
572                 if (done_count == 0) {
573                         return did_something - 1;
574                 }
575                 if (done_count != count) {
576                         n_read = done_count;
577                         goto again;
578                 }
579                 ta->tb_blockno = want_blockno;
580                 /* re-calcluate this, since record_size may be > block_size, in which
581                  * case the record_num may not change for each block read from tape */
582                 ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
583         }
584
585         record_off = ta->mover_want_pos % ta->mover_state.record_size;
586
587         n_avail = ta->mover_state.record_size - record_off;
588         if (n_read > n_avail)
589                 n_read = n_avail;
590         if (n_read != done_count) {
591                 dbprintf("lost %lu bytes %lu %u\n", done_count - n_read, done_count, n_read);
592                 n_read = done_count;
593         }
594
595         data = &ta->tape_buffer[record_off];
596
597         bcopy (data, ch->data + ch->end_ix, n_read);
598         ch->end_ix += n_read;
599         ta->mover_state.bytes_moved += n_read;
600         ta->mover_want_pos += n_read;
601         ta->mover_state.bytes_left_to_read -= n_read;
602
603         did_something++;
604
605         goto again;     /* do as much as possible */
606 }
607
608 void
609 ndmta_mover_send_notice (struct ndm_session *sess)
610 {
611         struct ndm_tape_agent * ta = &sess->tape_acb;
612
613         if (!ta->mover_notify_pending)
614                 return;
615
616         ta->mover_notify_pending = 0;
617
618         switch (ta->mover_state.state) {
619         case NDMP9_MOVER_STATE_HALTED:
620                 ndma_notify_mover_halted (sess);
621                 break;
622
623         case NDMP9_MOVER_STATE_PAUSED:
624                 ndma_notify_mover_paused (sess);
625                 break;
626
627         default:
628                 /* Hmm. Why are we here. Race? */
629                 break;
630         }
631 }
632
633 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */