Imported Upstream version 3.1.0
[debian/amanda] / ndmp-src / ndma_tape_simulator.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 int     simu_back_one (struct ndm_session *sess, int over_file_mark);
45 int     simu_forw_one (struct ndm_session *sess, int over_file_mark);
46 int     simu_flush_weof (struct ndm_session *sess);
47
48
49 #ifdef NDMOS_OPTION_TAPE_SIMULATOR
50
51 struct simu_gap {
52         u_long          magic;
53         u_long          rectype;
54         u_long          prev_size;
55         u_long          size;
56 };
57
58 #define SIMU_GAP_MAGIC          0x0BEEFEE0
59 #define SIMU_GAP_RT_(a,b,c,d) ((a<<0)+(b<<8)+(c<<16)+(d<<24))
60 #define SIMU_GAP_RT_BOT         SIMU_GAP_RT_('B','O','T','_')
61 #define SIMU_GAP_RT_DATA        SIMU_GAP_RT_('D','A','T','A')
62 #define SIMU_GAP_RT_FILE        SIMU_GAP_RT_('F','I','L','E')
63 #define SIMU_GAP_RT_EOT         SIMU_GAP_RT_('E','O','T','_')
64
65 /* send logical EOM with a bit less than 2 32k blocks left (due to SIMU_GAPs) */
66 #define TAPE_SIM_LOGICAL_EOM    32768*2
67
68 /* we sneak a peek at this global variable - probably not the best way, but
69  * it works */
70 extern off_t o_tape_limit;
71
72 int
73 ndmos_tape_initialize (struct ndm_session *sess)
74 {
75         struct ndm_tape_agent * ta = &sess->tape_acb;
76
77         ta->tape_fd = -1;
78         NDMOS_MACRO_ZEROFILL (&ta->tape_state);
79         ta->tape_state.error = NDMP9_DEV_NOT_OPEN_ERR;
80         ta->tape_state.state = NDMP9_TAPE_STATE_IDLE;
81
82         return 0;
83 }
84
85 static int
86 touch_tape_lockfile(char *drive_name)
87 {
88     char *lockfile_name;
89     int fd;
90
91     lockfile_name = g_strdup_printf("%s.lck", drive_name);
92     if ((fd = open(lockfile_name, O_CREAT|O_EXCL, 0666)) < 0) {
93         g_free(lockfile_name);
94         return -1;
95     }
96
97     close(fd);
98     g_free(lockfile_name);
99     return 0;
100 }
101
102 static void
103 unlink_tape_lockfile(char *drive_name)
104 {
105     char *lockfile_name;
106
107     lockfile_name = g_strdup_printf("%s.lck", drive_name);
108     unlink(lockfile_name);
109     g_free(lockfile_name);
110 }
111
112 ndmp9_error
113 ndmos_tape_open (struct ndm_session *sess, char *drive_name, int will_write)
114 {
115         struct ndm_tape_agent * ta = &sess->tape_acb;
116         struct simu_gap         gap;
117         struct stat             st;
118         int                     read_only, omode;
119         int                     rc, fd;
120         char                    *pos_symlink_name;
121         char                    pos_buf[32];
122         off_t                   pos = -1;
123
124         if (ta->tape_fd >= 0) {
125                 ndma_send_logmsg(sess, NDMP9_LOG_ERROR, sess->plumb.control,
126                          "device simulator is already open");
127                 return NDMP9_DEVICE_OPENED_ERR;
128         }
129
130         if (stat (drive_name, &st) < 0) {
131                 return NDMP9_NO_DEVICE_ERR;
132         }
133
134         read_only = (st.st_mode & 0222) == 0;
135
136         if (!will_write) {
137                 omode = 0;
138         } else {
139                 if (read_only)
140                         return NDMP9_WRITE_PROTECT_ERR;
141                 omode = 2;              /* ndmp_write means read/write */
142         }
143
144         if (touch_tape_lockfile(drive_name) < 0)
145             return NDMP9_DEVICE_BUSY_ERR;
146
147         fd = open (drive_name, omode);
148         if (fd < 0) {
149                 return NDMP9_PERMISSION_ERR;
150         }
151
152         pos_symlink_name = g_strdup_printf("%s.pos", drive_name);
153
154         if (st.st_size == 0) {
155                 remove (pos_symlink_name);
156                 if (will_write) {
157                         gap.magic = SIMU_GAP_MAGIC;
158                         gap.rectype = SIMU_GAP_RT_BOT;
159                         gap.size = 0;
160                         gap.prev_size = 0;
161                         if (write (fd, &gap, sizeof gap) < (int)sizeof gap) {
162                             close(fd);
163                             return NDMP9_IO_ERR;
164                         }
165
166                         gap.rectype = SIMU_GAP_RT_EOT;
167                         if (write (fd, &gap, sizeof gap) < (int)sizeof gap) {
168                             close(fd);
169                             return NDMP9_IO_ERR;
170                         }
171                         lseek (fd, (off_t)0, 0);
172                 } else {
173                         goto skip_header_check;
174                 }
175         }
176
177         rc = read (fd, &gap, sizeof gap);
178         if (rc != sizeof gap) {
179                 close (fd);
180                 return NDMP9_NO_TAPE_LOADED_ERR;
181         }
182
183 #if 1
184         if (gap.magic != SIMU_GAP_MAGIC) {
185                 close (fd);
186                 return NDMP9_IO_ERR;
187         }
188 #else
189         if (gap.magic != SIMU_GAP_MAGIC
190          || gap.rectype != SIMU_GAP_RT_BOT
191          || gap.size != 0) {
192                 close (fd);
193                 return NDMP9_IO_ERR;
194         }
195 #endif
196
197         rc = readlink (pos_symlink_name, pos_buf, sizeof pos_buf);
198         if (rc > 0) {
199                 pos_buf[rc] = 0;
200                 pos = strtol (pos_buf, 0, 0);
201                 lseek (fd, pos, 0);
202                 rc = read (fd, &gap, sizeof gap);
203                 if (rc == sizeof gap && gap.magic == SIMU_GAP_MAGIC) {
204                 } else {
205                         pos = sizeof gap;
206                 }
207                 lseek (fd, pos, 0);
208         }
209
210   skip_header_check:
211         remove (pos_symlink_name);
212         g_free(pos_symlink_name);
213
214         ta->tape_fd = fd;
215         NDMOS_API_BZERO (ta->drive_name, sizeof ta->drive_name);
216         g_strlcpy (ta->drive_name, drive_name, sizeof ta->drive_name);
217         bzero (&ta->tape_state, sizeof ta->tape_state);
218         ta->tape_state.error = NDMP9_NO_ERR;
219         ta->tape_state.state = NDMP9_TAPE_STATE_OPEN;
220         ta->tape_state.open_mode =
221                 will_write ? NDMP9_TAPE_RDWR_MODE : NDMP9_TAPE_READ_MODE;
222         ta->tape_state.file_num.valid = NDMP9_VALIDITY_VALID;
223         ta->tape_state.soft_errors.valid = NDMP9_VALIDITY_VALID;
224         ta->tape_state.block_size.valid = NDMP9_VALIDITY_VALID;
225         ta->tape_state.blockno.valid = NDMP9_VALIDITY_VALID;
226         ta->tape_state.total_space.valid = NDMP9_VALIDITY_INVALID;
227         ta->tape_state.space_remain.valid = NDMP9_VALIDITY_INVALID;
228
229         ta->sent_leom = 0;
230         if (o_tape_limit) {
231             g_assert(o_tape_limit > st.st_size);
232
233             ta->tape_state.total_space.valid = NDMP9_VALIDITY_VALID;
234             ta->tape_state.total_space.value = o_tape_limit;
235             ta->tape_state.space_remain.valid = NDMP9_VALIDITY_VALID;
236             ta->tape_state.space_remain.value = o_tape_limit - st.st_size;
237         }
238
239         return NDMP9_NO_ERR;
240 }
241
242 ndmp9_error
243 ndmos_tape_close (struct ndm_session *sess)
244 {
245         struct ndm_tape_agent * ta = &sess->tape_acb;
246         off_t                   cur_pos;
247
248         /* TODO this is not called on an EOF from the DMA, so the lockfile
249          * will remain, although the spec says the tape service should be
250          * automatically closed */
251
252         if (ta->tape_fd < 0) {
253                 return NDMP9_DEV_NOT_OPEN_ERR;
254         }
255
256         simu_flush_weof(sess);
257
258 #if 0
259         u_long                  resid;
260         ndmos_tape_mtio (sess, NDMP9_MTIO_REW, 1, &resid);
261 #endif
262
263         cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
264         if (cur_pos != -1) {
265                 char            *pos_symlink_name;
266                 char            pos_buf[32];
267
268                 pos_symlink_name = g_strdup_printf("%s.pos", ta->drive_name);
269                 sprintf (pos_buf, "%ld", (long) cur_pos);
270                 if (symlink (pos_buf, pos_symlink_name) < 0) {
271                     ; /* ignore error during close */
272                 }
273                 g_free(pos_symlink_name);
274         }
275
276         close (ta->tape_fd);
277         ta->tape_fd = -1;
278
279         unlink_tape_lockfile(ta->drive_name);
280
281         ndmos_tape_initialize (sess);
282
283         return NDMP9_NO_ERR;
284 }
285
286 void
287 ndmos_tape_sync_state (struct ndm_session *sess)
288 {
289         struct ndm_tape_agent * ta = &sess->tape_acb;
290
291         if (ta->tape_fd < 0) {
292                 ta->tape_state.error = NDMP9_DEV_NOT_OPEN_ERR;
293                 ta->tape_state.state = NDMP9_TAPE_STATE_IDLE;
294                 ta->tape_state.file_num.valid = NDMP9_VALIDITY_INVALID;
295                 ta->tape_state.soft_errors.valid = NDMP9_VALIDITY_INVALID;
296                 ta->tape_state.block_size.valid = NDMP9_VALIDITY_INVALID;
297                 ta->tape_state.blockno.valid = NDMP9_VALIDITY_INVALID;
298         } else {
299                 ta->tape_state.error = NDMP9_NO_ERR;
300                 if (ta->mover_state.state == NDMP9_MOVER_STATE_ACTIVE)
301                         ta->tape_state.state = NDMP9_TAPE_STATE_MOVER;
302                 else
303                         ta->tape_state.state = NDMP9_TAPE_STATE_OPEN;
304                 ta->tape_state.file_num.valid = NDMP9_VALIDITY_VALID;
305                 ta->tape_state.soft_errors.valid = NDMP9_VALIDITY_VALID;
306                 ta->tape_state.block_size.valid = NDMP9_VALIDITY_VALID;
307                 ta->tape_state.blockno.valid = NDMP9_VALIDITY_VALID;
308         }
309
310         return;
311 }
312
313 int
314 simu_back_one (struct ndm_session *sess, int over_file_mark)
315 {
316         struct ndm_tape_agent * ta = &sess->tape_acb;
317         struct simu_gap         gap;
318         off_t                   cur_pos;
319         off_t                   new_pos;
320         int                     rc;
321
322         cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
323
324         rc = read (ta->tape_fd, &gap, sizeof gap);
325         if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC)
326                 goto bail_out;
327
328         new_pos = cur_pos;
329         new_pos -= sizeof gap + gap.prev_size;
330
331         ta->sent_leom = 0;
332
333         /*
334          * This is the new position. We need to update simu_prev_gap.
335          */
336
337         lseek (ta->tape_fd, new_pos, 0);
338
339         rc = read (ta->tape_fd, &gap, sizeof gap);
340         if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC)
341                 goto bail_out;
342
343         switch (gap.rectype) {
344         case SIMU_GAP_RT_BOT:
345                 /* can't actually back up to this, but update stuff */
346                 ta->tape_state.file_num.value = 0;
347                 ta->tape_state.blockno.value = 0;
348                 /* cur_pos is now just right */
349                 return 0;               /* can't back up */
350
351         case SIMU_GAP_RT_EOT:
352                 /* this just isn't suppose to happen */
353                 goto bail_out;
354
355         case SIMU_GAP_RT_DATA:
356                 /* this is always OK */
357                 if (ta->tape_state.blockno.value > 0)
358                         ta->tape_state.blockno.value--;
359                 lseek (ta->tape_fd, new_pos, 0);
360                 return SIMU_GAP_RT_DATA;
361
362         case SIMU_GAP_RT_FILE:
363                 ta->tape_state.blockno.value = 0;
364                 if (!over_file_mark) {
365                         lseek (ta->tape_fd, cur_pos, 0);
366                         return 0;
367                 }
368                 if (ta->tape_state.file_num.value > 0)
369                         ta->tape_state.file_num.value--;
370                 lseek (ta->tape_fd, new_pos, 0);
371                 return SIMU_GAP_RT_FILE;
372
373         default:
374                 /* this just isn't suppose to happen */
375                 goto bail_out;
376         }
377
378   bail_out:
379         lseek (ta->tape_fd, cur_pos, 0);
380         return -1;
381 }
382
383 int
384 simu_forw_one (struct ndm_session *sess, int over_file_mark)
385 {
386         struct ndm_tape_agent * ta = &sess->tape_acb;
387         struct simu_gap         gap;
388         off_t                   cur_pos;
389         off_t                   new_pos;
390         int                     rc;
391
392         cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
393
394         rc = read (ta->tape_fd, &gap, sizeof gap);
395         if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC)
396                 goto bail_out;
397
398         ta->sent_leom = 0;
399
400         new_pos = cur_pos;
401         new_pos += gap.size + sizeof gap;
402
403         switch (gap.rectype) {
404         case SIMU_GAP_RT_BOT:
405                 /* this just isn't suppose to happen */
406                 goto bail_out;
407
408         case SIMU_GAP_RT_EOT:
409                 lseek (ta->tape_fd, cur_pos, 0);
410                 return 0;       /* can't go forward */
411
412         case SIMU_GAP_RT_DATA:
413                 /* this is always OK */
414                 ta->tape_state.blockno.value++;
415                 lseek (ta->tape_fd, new_pos, 0);
416                 return SIMU_GAP_RT_DATA;
417
418         case SIMU_GAP_RT_FILE:
419                 if (!over_file_mark) {
420                         lseek (ta->tape_fd, cur_pos, 0);
421                         return 0;
422                 }
423                 ta->tape_state.blockno.value = 0;
424                 ta->tape_state.file_num.value++;
425                 /* cur_pos is just right */
426                 return SIMU_GAP_RT_FILE;
427
428         default:
429                 /* this just isn't suppose to happen */
430                 goto bail_out;
431         }
432
433   bail_out:
434         lseek (ta->tape_fd, cur_pos, 0);
435         return -1;
436 }
437
438 int
439 simu_flush_weof (struct ndm_session *sess)
440 {
441         struct ndm_tape_agent * ta = &sess->tape_acb;
442
443         if (ta->weof_on_close) {
444                 /* best effort */
445                 ndmos_tape_wfm (sess);
446         }
447         return 0;
448 }
449                 
450
451 ndmp9_error
452 ndmos_tape_mtio (struct ndm_session *sess,
453   ndmp9_tape_mtio_op op, u_long count, u_long *resid)
454 {
455         struct ndm_tape_agent * ta = &sess->tape_acb;
456         int                     rc;
457
458         *resid = count;
459
460         if (ta->tape_fd < 0) {
461                 return NDMP9_DEV_NOT_OPEN_ERR;
462         }
463
464         /* audit for valid op and for tape mode */
465         switch (op) {
466         case NDMP9_MTIO_FSF:
467                 while (*resid > 0) {
468                         simu_flush_weof(sess);
469                         rc = simu_forw_one (sess, 1);
470                         if (rc < 0)
471                                 return NDMP9_IO_ERR;
472                         if (rc == 0)
473                                 break;
474                         if (rc == SIMU_GAP_RT_FILE)
475                                 *resid -= 1;
476                 }
477                 break;
478
479         case NDMP9_MTIO_BSF:
480                 while (*resid > 0) {
481                         simu_flush_weof(sess);
482                         rc = simu_back_one (sess, 1);
483                         if (rc < 0)
484                                 return NDMP9_IO_ERR;
485                         if (rc == 0)
486                                 break;
487                         if (rc == SIMU_GAP_RT_FILE)
488                                 *resid -= 1;
489                 }
490                 break;
491
492         case NDMP9_MTIO_FSR:
493                 while (*resid > 0) {
494                         simu_flush_weof(sess);
495                         rc = simu_forw_one (sess, 0);
496                         if (rc < 0)
497                                 return NDMP9_IO_ERR;
498                         if (rc == 0)
499                                 break;
500                         *resid -= 1;
501                 }
502                 break;
503
504         case NDMP9_MTIO_BSR:
505                 while (*resid > 0) {
506                         simu_flush_weof(sess);
507                         rc = simu_back_one (sess, 0);
508                         if (rc < 0)
509                                 return NDMP9_IO_ERR;
510                         if (rc == 0)
511                                 break;
512                         *resid -= 1;
513                 }
514                 break;
515
516         case NDMP9_MTIO_REW:
517                 simu_flush_weof(sess);
518                 *resid = 0;
519                 ta->tape_state.file_num.value = 0;
520                 ta->tape_state.blockno.value = 0;
521                 lseek (ta->tape_fd, (off_t)(sizeof (struct simu_gap)), 0);
522                 break;
523
524         case NDMP9_MTIO_OFF:
525                 simu_flush_weof(sess);
526                 /* Hmmm. */
527                 break;
528
529         case NDMP9_MTIO_EOF:            /* should be "WFM" write-file-mark */
530                 if (!NDMTA_TAPE_IS_WRITABLE(ta)) {
531                         return NDMP9_PERMISSION_ERR;
532                 }
533                 while (*resid > 0) {
534                         ndmp9_error     err;
535
536                         err = ndmos_tape_wfm (sess);
537                         if (err != NDMP9_NO_ERR)
538                                 return err;
539
540                         *resid -= 1;
541                 }
542                 break;
543
544         default:
545                 return NDMP9_ILLEGAL_ARGS_ERR;
546         }
547
548         return NDMP9_NO_ERR;
549 }
550
551 ndmp9_error
552 ndmos_tape_write (struct ndm_session *sess,
553   char *buf, u_long count, u_long *done_count)
554 {
555         struct ndm_tape_agent * ta = &sess->tape_acb;
556         int                     rc;
557         struct simu_gap         gap;
558         off_t                   cur_pos;
559         ndmp9_error             err;
560         u_long                  prev_size;
561
562         if (ta->tape_fd < 0) {
563                 return NDMP9_DEV_NOT_OPEN_ERR;
564         }
565
566         if (!NDMTA_TAPE_IS_WRITABLE(ta)) {
567                 return NDMP9_PERMISSION_ERR;
568         }
569
570         if (count == 0) {
571                 /*
572                  * NDMPv4 clarification -- a tape read or write with
573                  * a count==0 is a no-op. This is undoubtedly influenced
574                  * by the SCSI Sequential Access specification which
575                  * says much the same thing.
576                  */
577                 *done_count = 0;
578                 return NDMP9_NO_ERR;
579         }
580
581         cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
582
583         if (o_tape_limit) {
584             /* if cur_pos is past LEOM, but we haven't sent NDMP9_EOM_ERR yet,
585              * then do so now */
586             if (!ta->sent_leom && cur_pos > o_tape_limit - TAPE_SIM_LOGICAL_EOM) {
587                 ta->sent_leom = 1;
588                 return NDMP9_EOM_ERR;
589             }
590
591             /* if this write will put us over PEOM, then send NDMP9_IO_ERR */
592             if ((off_t)(cur_pos + sizeof gap + count) > o_tape_limit) {
593                 return NDMP9_IO_ERR;
594             }
595         }
596
597         rc = read (ta->tape_fd, &gap, sizeof gap);
598         if (rc != sizeof gap
599          || gap.magic != SIMU_GAP_MAGIC) {
600                 lseek (ta->tape_fd, cur_pos, 0);
601                 return NDMP9_IO_ERR;
602         }
603
604         prev_size = gap.prev_size;
605
606         gap.magic = SIMU_GAP_MAGIC;
607         gap.rectype = SIMU_GAP_RT_DATA;
608         gap.prev_size = prev_size;
609         gap.size = count;
610
611         lseek (ta->tape_fd, cur_pos, 0);
612
613         if (write (ta->tape_fd, &gap, sizeof gap) == sizeof gap
614          && (u_long)write (ta->tape_fd, buf, count) == count) {
615                 cur_pos += count + sizeof gap;
616
617                 prev_size = count;
618
619                 ta->tape_state.blockno.value++;
620
621                 *done_count = count;
622
623                 err = NDMP9_NO_ERR;
624         } else {
625                 err = NDMP9_IO_ERR;
626         }
627
628
629         if (ftruncate (ta->tape_fd, cur_pos) < 0)
630             return NDMP9_IO_ERR;
631
632         lseek (ta->tape_fd, cur_pos, 0);
633
634         gap.rectype = SIMU_GAP_RT_EOT;
635         gap.size = 0;
636         gap.prev_size = prev_size;
637
638         if (write (ta->tape_fd, &gap, sizeof gap) < (int)sizeof gap)
639             return NDMP9_IO_ERR;
640         lseek (ta->tape_fd, cur_pos, 0);
641
642         if (o_tape_limit) {
643             ta->tape_state.space_remain.value = o_tape_limit - cur_pos;
644         }
645
646         ta->weof_on_close = 1;
647
648         return err;
649 }
650
651 ndmp9_error
652 ndmos_tape_wfm (struct ndm_session *sess)
653 {
654         struct ndm_tape_agent * ta = &sess->tape_acb;
655         int                     rc;
656         struct simu_gap         gap;
657         off_t                   cur_pos;
658         ndmp9_error             err;
659         u_long                  prev_size;
660
661         ta->weof_on_close = 0;
662
663         if (ta->tape_fd < 0) {
664                 return NDMP9_DEV_NOT_OPEN_ERR;
665         }
666
667         if (!NDMTA_TAPE_IS_WRITABLE(ta)) {
668                 return NDMP9_PERMISSION_ERR;
669         }
670
671         cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
672
673         if (o_tape_limit) {
674             /* note: filemarks *never* trigger NDMP9_EOM_ERR */
675
676             /* if this write will put us over PEOM, then send NDMP9_IO_ERR */
677             if ((off_t)(cur_pos + sizeof gap) > o_tape_limit) {
678                 return NDMP9_IO_ERR;
679             }
680         }
681
682         rc = read (ta->tape_fd, &gap, sizeof gap);
683         if (rc != sizeof gap
684          || gap.magic != SIMU_GAP_MAGIC) {
685                 lseek (ta->tape_fd, cur_pos, 0);
686                 return NDMP9_IO_ERR;
687         }
688
689         prev_size = gap.prev_size;
690
691         gap.magic = SIMU_GAP_MAGIC;
692         gap.rectype = SIMU_GAP_RT_FILE;
693         gap.prev_size = prev_size;
694         gap.size = 0;
695
696         lseek (ta->tape_fd, cur_pos, 0);
697
698         if (write (ta->tape_fd, &gap, sizeof gap) == sizeof gap) {
699
700                 cur_pos += sizeof gap;
701
702                 prev_size = 0;
703
704                 ta->tape_state.file_num.value++;
705                 ta->tape_state.blockno.value = 0;
706
707                 err = NDMP9_NO_ERR;
708         } else {
709                 err = NDMP9_IO_ERR;
710         }
711
712         if (ftruncate (ta->tape_fd, cur_pos) < 0)
713             return NDMP9_IO_ERR;
714         lseek (ta->tape_fd, cur_pos, 0);
715
716         gap.rectype = SIMU_GAP_RT_EOT;
717         gap.size = 0;
718         gap.prev_size = prev_size;
719
720         if (write (ta->tape_fd, &gap, sizeof gap) < (int)sizeof gap)
721                 return NDMP9_IO_ERR;
722         lseek (ta->tape_fd, cur_pos, 0);
723
724         if (o_tape_limit) {
725             ta->tape_state.space_remain.value = o_tape_limit - cur_pos;
726         }
727
728         return err;
729 }
730
731 ndmp9_error
732 ndmos_tape_read (struct ndm_session *sess,
733   char *buf, u_long count, u_long *done_count)
734 {
735         struct ndm_tape_agent * ta = &sess->tape_acb;
736         int                     rc;
737         struct simu_gap         gap;
738         off_t                   cur_pos;
739
740         if (ta->tape_fd < 0) {
741                 return NDMP9_DEV_NOT_OPEN_ERR;
742         }
743
744         if (count == 0) {
745                 /*
746                  * NDMPv4 clarification -- a tape read or write with
747                  * a count==0 is a no-op. This is undoubtedly influenced
748                  * by the SCSI Sequential Access specification which
749                  * says much the same thing.
750                  */
751
752                 *done_count = 0;
753                 return NDMP9_NO_ERR;
754         }
755
756         cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
757
758         rc = read (ta->tape_fd, &gap, sizeof gap);
759         if (rc != sizeof gap
760          || gap.magic != SIMU_GAP_MAGIC) {
761                 lseek (ta->tape_fd, cur_pos, 0);
762                 return NDMP9_IO_ERR;
763         }
764
765         if (gap.rectype == SIMU_GAP_RT_DATA) {
766                 unsigned        nb;
767
768                 nb = count;
769                 if (nb > gap.size)
770                         nb = gap.size;
771
772                 rc = read (ta->tape_fd, buf, nb);
773                 if (rc != (int)nb) {
774                         lseek (ta->tape_fd, cur_pos, 0);
775                         return NDMP9_IO_ERR;
776                 }
777
778                 if (gap.size != nb) {
779                         cur_pos += sizeof gap + gap.size;
780                         lseek (ta->tape_fd, cur_pos, 0);
781                 }
782
783                 ta->tape_state.blockno.value++;
784
785                 *done_count = nb;
786         } else {
787                 /* all other record types are interpretted as EOF */
788                 lseek (ta->tape_fd, cur_pos, 0);
789                 *done_count = 0;
790                 return NDMP9_EOF_ERR;
791         }
792         return NDMP9_NO_ERR;
793 }
794
795 #endif /* NDMOS_OPTION_TAPE_SIMULATOR */
796
797 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */