2 * Copyright (c) 1998,1999,2000
3 * Traakan, Inc., Los Altos, CA
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice unmodified, this list of conditions, and the following
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.
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
38 #include "ndmagents.h"
41 #ifndef NDMOS_OPTION_NO_TAPE_AGENT
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);
49 #ifdef NDMOS_OPTION_TAPE_SIMULATOR
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','_')
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
68 /* we sneak a peek at this global variable - probably not the best way, but
70 extern off_t o_tape_limit;
73 ndmos_tape_initialize (struct ndm_session *sess)
75 struct ndm_tape_agent * ta = &sess->tape_acb;
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;
86 touch_tape_lockfile(char *drive_name)
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);
98 g_free(lockfile_name);
103 unlink_tape_lockfile(char *drive_name)
107 lockfile_name = g_strdup_printf("%s.lck", drive_name);
108 unlink(lockfile_name);
109 g_free(lockfile_name);
113 ndmos_tape_open (struct ndm_session *sess, char *drive_name, int will_write)
115 struct ndm_tape_agent * ta = &sess->tape_acb;
118 int read_only, omode;
120 char *pos_symlink_name;
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;
130 if (stat (drive_name, &st) < 0) {
131 return NDMP9_NO_DEVICE_ERR;
134 read_only = (st.st_mode & 0222) == 0;
140 return NDMP9_WRITE_PROTECT_ERR;
141 omode = 2; /* ndmp_write means read/write */
144 if (touch_tape_lockfile(drive_name) < 0)
145 return NDMP9_DEVICE_BUSY_ERR;
147 fd = open (drive_name, omode);
149 return NDMP9_PERMISSION_ERR;
152 pos_symlink_name = g_strdup_printf("%s.pos", drive_name);
154 if (st.st_size == 0) {
155 remove (pos_symlink_name);
157 gap.magic = SIMU_GAP_MAGIC;
158 gap.rectype = SIMU_GAP_RT_BOT;
161 if (write (fd, &gap, sizeof gap) < (int)sizeof gap) {
166 gap.rectype = SIMU_GAP_RT_EOT;
167 if (write (fd, &gap, sizeof gap) < (int)sizeof gap) {
171 lseek (fd, (off_t)0, 0);
173 goto skip_header_check;
177 rc = read (fd, &gap, sizeof gap);
178 if (rc != sizeof gap) {
180 return NDMP9_NO_TAPE_LOADED_ERR;
184 if (gap.magic != SIMU_GAP_MAGIC) {
189 if (gap.magic != SIMU_GAP_MAGIC
190 || gap.rectype != SIMU_GAP_RT_BOT
197 rc = readlink (pos_symlink_name, pos_buf, sizeof pos_buf);
200 pos = strtol (pos_buf, 0, 0);
202 rc = read (fd, &gap, sizeof gap);
203 if (rc == sizeof gap && gap.magic == SIMU_GAP_MAGIC) {
211 remove (pos_symlink_name);
212 g_free(pos_symlink_name);
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;
231 g_assert(o_tape_limit > st.st_size);
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;
243 ndmos_tape_close (struct ndm_session *sess)
245 struct ndm_tape_agent * ta = &sess->tape_acb;
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 */
252 if (ta->tape_fd < 0) {
253 return NDMP9_DEV_NOT_OPEN_ERR;
256 simu_flush_weof(sess);
260 ndmos_tape_mtio (sess, NDMP9_MTIO_REW, 1, &resid);
263 cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
265 char *pos_symlink_name;
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 */
273 g_free(pos_symlink_name);
279 unlink_tape_lockfile(ta->drive_name);
281 ndmos_tape_initialize (sess);
287 ndmos_tape_sync_state (struct ndm_session *sess)
289 struct ndm_tape_agent * ta = &sess->tape_acb;
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;
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;
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;
314 simu_back_one (struct ndm_session *sess, int over_file_mark)
316 struct ndm_tape_agent * ta = &sess->tape_acb;
322 cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
324 rc = read (ta->tape_fd, &gap, sizeof gap);
325 if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC)
329 new_pos -= sizeof gap + gap.prev_size;
334 * This is the new position. We need to update simu_prev_gap.
337 lseek (ta->tape_fd, new_pos, 0);
339 rc = read (ta->tape_fd, &gap, sizeof gap);
340 if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC)
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 */
351 case SIMU_GAP_RT_EOT:
352 /* this just isn't suppose to happen */
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;
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);
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;
374 /* this just isn't suppose to happen */
379 lseek (ta->tape_fd, cur_pos, 0);
384 simu_forw_one (struct ndm_session *sess, int over_file_mark)
386 struct ndm_tape_agent * ta = &sess->tape_acb;
392 cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
394 rc = read (ta->tape_fd, &gap, sizeof gap);
395 if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC)
401 new_pos += gap.size + sizeof gap;
403 switch (gap.rectype) {
404 case SIMU_GAP_RT_BOT:
405 /* this just isn't suppose to happen */
408 case SIMU_GAP_RT_EOT:
409 lseek (ta->tape_fd, cur_pos, 0);
410 return 0; /* can't go forward */
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;
418 case SIMU_GAP_RT_FILE:
419 if (!over_file_mark) {
420 lseek (ta->tape_fd, cur_pos, 0);
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;
429 /* this just isn't suppose to happen */
434 lseek (ta->tape_fd, cur_pos, 0);
439 simu_flush_weof (struct ndm_session *sess)
441 struct ndm_tape_agent * ta = &sess->tape_acb;
443 if (ta->weof_on_close) {
445 ndmos_tape_wfm (sess);
452 ndmos_tape_mtio (struct ndm_session *sess,
453 ndmp9_tape_mtio_op op, u_long count, u_long *resid)
455 struct ndm_tape_agent * ta = &sess->tape_acb;
460 if (ta->tape_fd < 0) {
461 return NDMP9_DEV_NOT_OPEN_ERR;
464 /* audit for valid op and for tape mode */
468 simu_flush_weof(sess);
469 rc = simu_forw_one (sess, 1);
474 if (rc == SIMU_GAP_RT_FILE)
481 simu_flush_weof(sess);
482 rc = simu_back_one (sess, 1);
487 if (rc == SIMU_GAP_RT_FILE)
494 simu_flush_weof(sess);
495 rc = simu_forw_one (sess, 0);
506 simu_flush_weof(sess);
507 rc = simu_back_one (sess, 0);
517 simu_flush_weof(sess);
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);
525 simu_flush_weof(sess);
529 case NDMP9_MTIO_EOF: /* should be "WFM" write-file-mark */
530 if (!NDMTA_TAPE_IS_WRITABLE(ta)) {
531 return NDMP9_PERMISSION_ERR;
536 err = ndmos_tape_wfm (sess);
537 if (err != NDMP9_NO_ERR)
545 return NDMP9_ILLEGAL_ARGS_ERR;
552 ndmos_tape_write (struct ndm_session *sess,
553 char *buf, u_long count, u_long *done_count)
555 struct ndm_tape_agent * ta = &sess->tape_acb;
562 if (ta->tape_fd < 0) {
563 return NDMP9_DEV_NOT_OPEN_ERR;
566 if (!NDMTA_TAPE_IS_WRITABLE(ta)) {
567 return NDMP9_PERMISSION_ERR;
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.
581 cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
584 /* if cur_pos is past LEOM, but we haven't sent NDMP9_EOM_ERR yet,
586 if (!ta->sent_leom && cur_pos > o_tape_limit - TAPE_SIM_LOGICAL_EOM) {
588 return NDMP9_EOM_ERR;
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) {
597 rc = read (ta->tape_fd, &gap, sizeof gap);
599 || gap.magic != SIMU_GAP_MAGIC) {
600 lseek (ta->tape_fd, cur_pos, 0);
604 prev_size = gap.prev_size;
606 gap.magic = SIMU_GAP_MAGIC;
607 gap.rectype = SIMU_GAP_RT_DATA;
608 gap.prev_size = prev_size;
611 lseek (ta->tape_fd, cur_pos, 0);
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;
619 ta->tape_state.blockno.value++;
629 if (ftruncate (ta->tape_fd, cur_pos) < 0)
632 lseek (ta->tape_fd, cur_pos, 0);
634 gap.rectype = SIMU_GAP_RT_EOT;
636 gap.prev_size = prev_size;
638 if (write (ta->tape_fd, &gap, sizeof gap) < (int)sizeof gap)
640 lseek (ta->tape_fd, cur_pos, 0);
643 ta->tape_state.space_remain.value = o_tape_limit - cur_pos;
646 ta->weof_on_close = 1;
652 ndmos_tape_wfm (struct ndm_session *sess)
654 struct ndm_tape_agent * ta = &sess->tape_acb;
661 ta->weof_on_close = 0;
663 if (ta->tape_fd < 0) {
664 return NDMP9_DEV_NOT_OPEN_ERR;
667 if (!NDMTA_TAPE_IS_WRITABLE(ta)) {
668 return NDMP9_PERMISSION_ERR;
671 cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
674 /* note: filemarks *never* trigger NDMP9_EOM_ERR */
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) {
682 rc = read (ta->tape_fd, &gap, sizeof gap);
684 || gap.magic != SIMU_GAP_MAGIC) {
685 lseek (ta->tape_fd, cur_pos, 0);
689 prev_size = gap.prev_size;
691 gap.magic = SIMU_GAP_MAGIC;
692 gap.rectype = SIMU_GAP_RT_FILE;
693 gap.prev_size = prev_size;
696 lseek (ta->tape_fd, cur_pos, 0);
698 if (write (ta->tape_fd, &gap, sizeof gap) == sizeof gap) {
700 cur_pos += sizeof gap;
704 ta->tape_state.file_num.value++;
705 ta->tape_state.blockno.value = 0;
712 if (ftruncate (ta->tape_fd, cur_pos) < 0)
714 lseek (ta->tape_fd, cur_pos, 0);
716 gap.rectype = SIMU_GAP_RT_EOT;
718 gap.prev_size = prev_size;
720 if (write (ta->tape_fd, &gap, sizeof gap) < (int)sizeof gap)
722 lseek (ta->tape_fd, cur_pos, 0);
725 ta->tape_state.space_remain.value = o_tape_limit - cur_pos;
732 ndmos_tape_read (struct ndm_session *sess,
733 char *buf, u_long count, u_long *done_count)
735 struct ndm_tape_agent * ta = &sess->tape_acb;
740 if (ta->tape_fd < 0) {
741 return NDMP9_DEV_NOT_OPEN_ERR;
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.
756 cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
758 rc = read (ta->tape_fd, &gap, sizeof gap);
760 || gap.magic != SIMU_GAP_MAGIC) {
761 lseek (ta->tape_fd, cur_pos, 0);
765 if (gap.rectype == SIMU_GAP_RT_DATA) {
772 rc = read (ta->tape_fd, buf, nb);
774 lseek (ta->tape_fd, cur_pos, 0);
778 if (gap.size != nb) {
779 cur_pos += sizeof gap + gap.size;
780 lseek (ta->tape_fd, cur_pos, 0);
783 ta->tape_state.blockno.value++;
787 /* all other record types are interpretted as EOF */
788 lseek (ta->tape_fd, cur_pos, 0);
790 return NDMP9_EOF_ERR;
795 #endif /* NDMOS_OPTION_TAPE_SIMULATOR */
797 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */