1 /* fhandler_tape.cc. See fhandler.h for a description of the fhandler
4 Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
14 #include <sys/termios.h>
18 #include <sys/param.h>
19 #include <ddk/ntddstor.h>
21 #include "perprocess.h"
27 #include "shared_info.h"
31 /* Media changes and bus resets are sometimes reported and the function
32 hasn't been executed. We repeat all functions which return with one
33 of these error codes. */
34 #define TAPE_FUNC(func) while ((lasterr = (func)) == ERROR_MEDIA_CHANGED) \
36 initialize (drive, false); \
37 part (partition)->initialize (0); \
40 #define IS_BOT(err) ((err) == ERROR_BEGINNING_OF_MEDIA)
42 #define IS_EOF(err) ((err) == ERROR_FILEMARK_DETECTED \
43 || (err) == ERROR_SETMARK_DETECTED)
45 #define IS_SM(err) ((err) == ERROR_SETMARK_DETECTED)
47 #define IS_EOD(err) ((err) == ERROR_END_OF_MEDIA \
48 || (err) == ERROR_EOM_OVERFLOW \
49 || (err) == ERROR_NO_DATA_DETECTED)
51 #define IS_EOM(err) ((err) == ERROR_END_OF_MEDIA \
52 || (err) == ERROR_EOM_OVERFLOW)
54 /**********************************************************************/
58 mtinfo_part::initialize (long nblock)
69 /**********************************************************************/
73 mtinfo_drive::initialize (int num, bool first_time)
89 for (int i = 0; i < MAX_PARTITION_NUM; ++i)
90 part (i)->initialize ();
94 mtinfo_drive::get_dp (HANDLE mt)
96 DWORD len = sizeof _dp;
97 TAPE_FUNC (GetTapeParameters (mt, GET_TAPE_DRIVE_INFORMATION, &len, &_dp));
98 return error ("get_dp");
102 mtinfo_drive::get_mp (HANDLE mt)
104 DWORD len = sizeof _mp;
105 TAPE_FUNC (GetTapeParameters (mt, GET_TAPE_MEDIA_INFORMATION, &len, &_mp));
106 return error ("get_mp");
110 mtinfo_drive::open (HANDLE mt)
115 if (partition < MAX_PARTITION_NUM && part (partition)->block != block)
116 part (partition)->initialize (block);
117 /* The following rewind in position 0 solves a problem which appears
118 * in case of multi volume archives (at least on NT4): The last ReadFile
119 * on the previous medium returns ERROR_NO_DATA_DETECTED. After media
120 * change, all subsequent ReadFile calls return ERROR_NO_DATA_DETECTED,
121 * too. The call to set_pos apparently reset some internal flags.
122 * FIXME: Is that really true or based on a misinterpretation? */
125 debug_printf ("rewind in position 0");
126 set_pos (mt, TAPE_REWIND, 0, false);
128 return error ("open");
132 mtinfo_drive::close (HANDLE mt, bool rewind)
135 if (GetTapeStatus (mt) == ERROR_NO_MEDIA_IN_DRIVE)
137 if (dirty >= has_written)
139 /* If an async write is still pending, wait for completion. */
140 if (dirty == async_write_pending)
141 lasterr = async_wait (mt, NULL);
144 /* if last operation was writing, write a filemark */
145 debug_printf ("writing filemark");
146 write_marks (mt, TAPE_FILEMARKS, two_fm () ? 2 : 1);
147 if (two_fm () && !lasterr && !rewind) /* Backspace over 2nd fmark. */
149 set_pos (mt, TAPE_SPACE_FILEMARKS, -1, false);
151 part (partition)->fblock = 0; /* That's obvious, isn't it? */
155 else if (dirty == has_read && !rewind)
159 /* Under SYSV semantics, the tape is moved past the next file mark
161 if (part (partition)->emark == no_eof)
162 set_pos (mt, TAPE_SPACE_FILEMARKS, 1, false);
163 else if (part (partition)->emark == eof_hit)
164 part (partition)->emark = eof;
168 /* Under BSD semantics, we must check if the filemark has been
169 inadvertendly crossed. If so cross the filemark backwards
170 and position the tape right before EOF. */
171 if (part (partition)->emark == eof_hit)
172 set_pos (mt, TAPE_SPACE_FILEMARKS, -1, false);
177 debug_printf ("rewinding");
178 set_pos (mt, TAPE_REWIND, 0, false);
180 if (auto_lock () && lock == auto_locked)
181 prepare (mt, TAPE_UNLOCK);
183 return error ("close");
187 mtinfo_drive::read (HANDLE mt, HANDLE mt_evt, void *ptr, size_t &ulen)
190 DWORD bytes_read = 0;
192 if (GetTapeStatus (mt) == ERROR_NO_MEDIA_IN_DRIVE)
193 return lasterr = ERROR_NO_MEDIA_IN_DRIVE;
194 if (lasterr == ERROR_BUS_RESET)
199 /* If an async write is still pending, wait for completion. */
200 if (dirty == async_write_pending)
201 lasterr = async_wait (mt, NULL);
203 if (part (partition)->emark == eof_hit)
205 part (partition)->emark = eof;
209 else if (part (partition)->emark == eod_hit)
211 part (partition)->emark = eod;
215 else if (part (partition)->emark == eod)
217 lasterr = ERROR_NO_DATA_DETECTED;
221 else if (part (partition)->emark == eom_hit)
223 part (partition)->emark = eom;
227 else if (part (partition)->emark == eom)
229 lasterr = ERROR_END_OF_MEDIA;
233 part (partition)->smark = false;
234 if (auto_lock () && lock < auto_locked)
235 prepare (mt, TAPE_LOCK, true);
236 ov.Offset = ov.OffsetHigh = 0;
238 ret = ReadFile (mt, ptr, ulen, &bytes_read, &ov);
239 lasterr = ret ? 0 : GetLastError ();
240 if (lasterr == ERROR_IO_PENDING)
241 lasterr = async_wait (mt, &bytes_read);
242 ulen = (size_t) bytes_read;
245 long blocks_read = mp ()->BlockSize == 0
246 ? 1 : howmany (bytes_read, mp ()->BlockSize);
247 block += blocks_read;
248 part (partition)->block += blocks_read;
249 if (part (partition)->fblock >= 0)
250 part (partition)->fblock += blocks_read;
252 if (IS_EOF (lasterr))
255 part (partition)->block++;
256 if (part (partition)->file >= 0)
257 part (partition)->file++;
258 part (partition)->fblock = 0;
259 part (partition)->smark = IS_SM (lasterr);
260 part (partition)->emark = bytes_read > 0 ? eof_hit : eof;
263 else if (IS_EOD (lasterr))
265 if (part (partition)->emark == eof)
266 part (partition)->emark = IS_EOM (lasterr) ? eom : eod;
269 part (partition)->emark = IS_EOM (lasterr) ? eom_hit : eod_hit;
275 part (partition)->emark = no_eof;
276 /* This happens if the buffer is too small when in variable block
277 size mode. Linux returns ENOMEM here. We're doing the same. */
278 if (lasterr == ERROR_MORE_DATA)
279 lasterr = ERROR_NOT_ENOUGH_MEMORY;
284 return error ("read");
288 mtinfo_drive::async_wait (HANDLE mt, DWORD *bytes_written)
292 bool ret = GetOverlappedResult (mt, &ov, &written, TRUE);
294 *bytes_written = written;
295 return ret ? 0 : GetLastError ();
299 mtinfo_drive::write (HANDLE mt, HANDLE mt_evt, const void *ptr, size_t &len)
302 DWORD bytes_written = 0;
305 if (GetTapeStatus (mt) == ERROR_NO_MEDIA_IN_DRIVE)
306 return lasterr = ERROR_NO_MEDIA_IN_DRIVE;
307 if (lasterr == ERROR_BUS_RESET)
310 return error ("write");
312 if (dirty == async_write_pending)
313 async_err = async_wait (mt, &bytes_written);
315 part (partition)->smark = false;
316 if (auto_lock () && lock < auto_locked)
317 prepare (mt, TAPE_LOCK, true);
318 ov.Offset = ov.OffsetHigh = 0;
320 ret = WriteFile (mt, ptr, len, &bytes_written, &ov);
321 lasterr = ret ? 0: GetLastError ();
322 if (lasterr == ERROR_IO_PENDING)
324 if (async_writes () && mp ()->BlockSize == 0)
325 dirty = async_write_pending;
327 /* Wait for completion if a non-async write. */
328 lasterr = async_wait (mt, &bytes_written);
330 len = (size_t) bytes_written;
331 if (bytes_written > 0)
333 long blocks_written = mp ()->BlockSize == 0
334 ? 1 : howmany (bytes_written, mp ()->BlockSize);
335 block += blocks_written;
336 part (partition)->block += blocks_written;
337 if (part (partition)->fblock >= 0)
338 part (partition)->fblock += blocks_written;
340 if (!lasterr && async_err)
342 if (lasterr == ERROR_EOM_OVERFLOW)
343 part (partition)->emark = eom;
344 else if (lasterr == ERROR_END_OF_MEDIA)
345 ; // FIXME?: part (partition)->emark = eom_hit;
348 part (partition)->emark = no_eof;
351 else if (lasterr == ERROR_IO_PENDING)
352 dirty = async_write_pending;
354 return error ("write");
358 mtinfo_drive::get_pos (HANDLE mt, long *ppartition, long *pblock)
362 TAPE_FUNC (GetTapePosition (mt, TAPE_LOGICAL_POSITION, &p, &low, &high));
363 if (lasterr == ERROR_INVALID_FUNCTION)
364 TAPE_FUNC (GetTapePosition (mt, TAPE_ABSOLUTE_POSITION, &p, &low, &high));
368 partition = (long) p - 1;
371 *ppartition= partition;
380 return error ("get_pos");
384 mtinfo_drive::_set_pos (HANDLE mt, int mode, long count, int partition,
387 /* If an async write is still pending, wait for completion. */
388 if (dirty == async_write_pending)
389 lasterr = async_wait (mt, NULL);
391 TAPE_FUNC (SetTapePosition (mt, mode, partition, count, count < 0 ? -1 : 0,
397 mtinfo_drive::set_pos (HANDLE mt, int mode, long count,
402 BOOL dont_wait = FALSE;
406 case TAPE_SPACE_RELATIVE_BLOCKS:
407 case TAPE_SPACE_FILEMARKS:
408 case TAPE_SPACE_SETMARKS:
415 case TAPE_ABSOLUTE_BLOCK:
416 case TAPE_LOGICAL_BLOCK:
418 dont_wait = nowait () ? TRUE : FALSE;
421 if (mode == TAPE_SPACE_FILEMARKS)
423 while (!err && undone > 0)
424 if (!(err = _set_pos (mt, mode, 1, 0, FALSE)) || IS_SM (err))
426 while (!err && undone < 0)
427 if (!(err = _set_pos (mt, mode, -1, 0, FALSE)) || IS_SM (err))
431 err = _set_pos (mt, mode, count, 0, dont_wait);
434 case TAPE_ABSOLUTE_BLOCK:
435 case TAPE_LOGICAL_BLOCK:
437 part (partition)->initialize (block);
443 part (partition)->initialize (0);
448 part (partition)->initialize (block);
451 case TAPE_SPACE_END_OF_DATA:
453 part (partition)->initialize (block);
454 part (partition)->emark = IS_EOM (err) ? eom : eod;
456 case TAPE_SPACE_FILEMARKS:
457 if (!err || IS_SM (err))
460 part (partition)->block = block;
463 if (part (partition)->file >= 0)
464 part (partition)->file += count - undone;
465 part (partition)->fblock = 0;
466 part (partition)->smark = IS_SM (err);
470 if (part (partition)->file >= 0)
471 part (partition)->file += count - undone;
472 part (partition)->fblock = -1;
473 part (partition)->smark = false;
476 err = set_pos (mt, mode, count > 0 ? -1 : 1, false);
478 part (partition)->emark = count > 0 ? eof : no_eof;
480 else if (IS_EOD (err))
483 part (partition)->block = block;
484 if (part (partition)->file >= 0)
485 part (partition)->file += count - undone;
486 part (partition)->fblock = -1;
487 part (partition)->smark = false;
488 part (partition)->emark = IS_EOM (err) ? eom : eod;
490 else if (IS_BOT (err))
493 part (partition)->initialize (0);
498 part (partition)->initialize (block);
501 case TAPE_SPACE_RELATIVE_BLOCKS:
505 part (partition)->block += count;
506 if (part (partition)->fblock >= 0)
507 part (partition)->fblock += count;
508 part (partition)->smark = false;
509 part (partition)->emark = no_eof;
511 else if (IS_EOF (err))
514 part (partition)->block = block;
515 if (part (partition)->file >= 0)
516 part (partition)->file += count > 0 ? 1 : -1;
517 part (partition)->fblock = count > 0 ? 0 : -1;
518 part (partition)->smark = (count > 0 && IS_SM (err));
519 part (partition)->emark = count > 0 ? eof : no_eof;
521 else if (IS_EOD (err))
524 part (partition)->fblock = block - part (partition)->block;
525 part (partition)->block = block;
526 part (partition)->smark = false;
527 part (partition)->emark = IS_EOM (err) ? eom : eod;
529 else if (IS_BOT (err))
532 part (partition)->initialize (0);
535 case TAPE_SPACE_SETMARKS:
537 part (partition)->block = block;
540 part (partition)->file = -1;
541 part (partition)->fblock = -1;
542 part (partition)->smark = true;
548 return error ("set_pos");
552 mtinfo_drive::create_partitions (HANDLE mt, long count)
554 if (dp ()->MaximumPartitionCount <= 1)
555 return ERROR_INVALID_PARAMETER;
556 if (set_pos (mt, TAPE_REWIND, 0, false))
559 part (partition)->initialize (0);
560 debug_printf ("Format tape with %s partition(s)", count <= 0 ? "one" : "two");
561 if (get_feature (TAPE_DRIVE_INITIATOR))
564 TAPE_FUNC (CreateTapePartition (mt, TAPE_INITIATOR_PARTITIONS,
565 count <= 0 ? 0 : 2, (DWORD) count));
567 else if (get_feature (TAPE_DRIVE_FIXED))
569 /* This is supposed to work for Tandberg SLR drivers up to version
570 1.6 which missed to set the TAPE_DRIVE_INITIATOR flag. According
571 to Tandberg, CreateTapePartition(TAPE_FIXED_PARTITIONS) apparently
572 does not ignore the dwCount parameter. Go figure! */
573 TAPE_FUNC (CreateTapePartition (mt, TAPE_FIXED_PARTITIONS,
574 count <= 0 ? 0 : 2, (DWORD) count));
577 lasterr = ERROR_INVALID_PARAMETER;
579 return error ("partition");
583 mtinfo_drive::set_partition (HANDLE mt, long count)
585 if (count < 0 || (unsigned long) count >= MAX_PARTITION_NUM)
586 lasterr = ERROR_INVALID_PARAMETER;
587 else if ((DWORD) count >= dp ()->MaximumPartitionCount)
588 lasterr = ERROR_IO_DEVICE;
591 int part_block = part (count)->block >= 0 ? part (count)->block : 0;
592 int err = _set_pos (mt, TAPE_LOGICAL_BLOCK, part_block, count + 1, FALSE);
595 int sav_block = block;
596 int sav_partition = partition;
598 if (sav_partition != partition)
600 if (partition < MAX_PARTITION_NUM
601 && part (partition)->block != block)
602 part (partition)->initialize (block);
604 else if (sav_block != block && partition < MAX_PARTITION_NUM)
605 part (partition)->initialize (block);
611 if (part (partition)->block == -1)
612 part (partition)->initialize (0);
615 return error ("set_partition");
619 mtinfo_drive::write_marks (HANDLE mt, int marktype, DWORD count)
621 /* If an async write is still pending, wait for completion. */
622 if (dirty == async_write_pending)
624 lasterr = async_wait (mt, NULL);
627 if (marktype != TAPE_SETMARKS)
629 if (marktype == TAPE_FILEMARKS
630 && !get_feature (TAPE_DRIVE_WRITE_FILEMARKS))
632 if (get_feature (TAPE_DRIVE_WRITE_LONG_FMKS))
633 marktype = TAPE_LONG_FILEMARKS;
635 marktype = TAPE_SHORT_FILEMARKS;
637 TAPE_FUNC (WriteTapemark (mt, marktype, count, FALSE));
642 part (partition)->block += count;
643 if (part (partition)->file >= 0)
644 part (partition)->file += count;
645 part (partition)->fblock = 0;
646 part (partition)->emark = eof;
647 part (partition)->smark = (marktype == TAPE_SETMARKS);
651 int sav_block = block;
652 int sav_partition = partition;
654 if (sav_partition != partition)
656 if (partition < MAX_PARTITION_NUM
657 && part (partition)->block != block)
658 part (partition)->initialize (block);
660 else if (sav_block != block && partition < MAX_PARTITION_NUM)
661 part (partition)->initialize (block);
664 return error ("write_marks");
668 mtinfo_drive::erase (HANDLE mt, int mode)
672 case TAPE_ERASE_SHORT:
673 if (!get_feature (TAPE_DRIVE_ERASE_SHORT))
674 mode = TAPE_ERASE_LONG;
676 case TAPE_ERASE_LONG:
677 if (!get_feature (TAPE_DRIVE_ERASE_LONG))
678 mode = TAPE_ERASE_SHORT;
681 TAPE_FUNC (EraseTape (mt, mode, nowait () ? TRUE : FALSE));
682 part (partition)->initialize (0);
683 return error ("erase");
687 mtinfo_drive::prepare (HANDLE mt, int action, bool is_auto)
689 BOOL dont_wait = FALSE;
691 /* If an async write is still pending, wait for completion. */
692 if (dirty == async_write_pending)
693 lasterr = async_wait (mt, NULL);
695 if (action == TAPE_UNLOAD || action == TAPE_LOAD || action == TAPE_TENSION)
696 dont_wait = nowait () ? TRUE : FALSE;
697 TAPE_FUNC (PrepareTape (mt, action, dont_wait));
698 /* Reset buffer after all successful preparations but lock and unlock. */
704 initialize (drive, false);
707 part (partition)->initialize (0);
710 lock = lasterr ? lock_error : is_auto ? auto_locked : locked;
713 lock = lasterr ? lock_error : unlocked;
716 return error ("prepare");
720 mtinfo_drive::set_compression (HANDLE mt, long count)
722 if (!get_feature (TAPE_DRIVE_SET_COMPRESSION))
723 return ERROR_INVALID_PARAMETER;
724 TAPE_SET_DRIVE_PARAMETERS sdp =
727 count ? TRUE : FALSE,
729 dp ()->ReportSetmarks,
730 dp ()->EOTWarningZoneSize
732 TAPE_FUNC (SetTapeParameters (mt, SET_TAPE_DRIVE_INFORMATION, &sdp));
735 dp ()->Compression = sdp.Compression;
739 return error ("set_compression");
743 mtinfo_drive::set_blocksize (HANDLE mt, long count)
745 TAPE_SET_MEDIA_PARAMETERS smp = {count};
746 TAPE_FUNC (SetTapeParameters (mt, SET_TAPE_MEDIA_INFORMATION, &smp));
747 return error ("set_blocksize");
751 mtinfo_drive::get_status (HANDLE mt, struct mtget *get)
757 return ERROR_INVALID_PARAMETER;
759 if ((tstat = GetTapeStatus (mt)) == ERROR_NO_MEDIA_IN_DRIVE)
762 memset (get, 0, sizeof *get);
764 get->mt_type = MT_ISUNKNOWN;
766 if (!notape && get_feature (TAPE_DRIVE_SET_BLOCK_SIZE))
767 get->mt_dsreg = (mp ()->BlockSize << MT_ST_BLKSIZE_SHIFT)
768 & MT_ST_BLKSIZE_MASK;
770 get->mt_dsreg = (dp ()->DefaultBlockSize << MT_ST_BLKSIZE_SHIFT)
771 & MT_ST_BLKSIZE_MASK;
773 if (wincap.has_ioctl_storage_get_media_types_ex ())
775 DWORD size = sizeof (GET_MEDIA_TYPES) + 10 * sizeof (DEVICE_MEDIA_INFO);
776 void *buf = alloca (size);
777 if (DeviceIoControl (mt, IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
778 NULL, 0, buf, size, &size, NULL)
779 || GetLastError () == ERROR_MORE_DATA)
781 PGET_MEDIA_TYPES gmt = (PGET_MEDIA_TYPES) buf;
782 for (DWORD i = 0; i < gmt->MediaInfoCount; ++i)
784 PDEVICE_MEDIA_INFO dmi = &gmt->MediaInfo[i];
785 get->mt_type = dmi->DeviceSpecific.TapeInfo.MediaType;
786 #define TINFO DeviceSpecific.TapeInfo
787 if (dmi->TINFO.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED)
789 get->mt_type = dmi->DeviceSpecific.TapeInfo.MediaType;
790 if (dmi->TINFO.BusType == BusTypeScsi)
792 (dmi->TINFO.BusSpecificData.ScsiInformation.DensityCode
793 << MT_ST_DENSITY_SHIFT)
794 & MT_ST_DENSITY_MASK;
804 get->mt_resid = partition;
805 get->mt_fileno = part (partition)->file;
806 get->mt_blkno = part (partition)->fblock;
808 if (get->mt_blkno == 0)
809 if (get->mt_fileno == 0)
810 get->mt_gstat |= GMT_BOT (-1);
812 get->mt_gstat |= GMT_EOF (-1);
813 if (part (partition)->emark >= eod_hit)
814 get->mt_gstat |= GMT_EOD (-1);
815 if (part (partition)->emark >= eom_hit)
816 get->mt_gstat |= GMT_EOT (-1);
818 if (part (partition)->smark)
819 get->mt_gstat |= GMT_SM (-1);
821 get->mt_gstat |= GMT_ONLINE (-1);
823 if (mp ()->WriteProtected)
824 get->mt_gstat |= GMT_WR_PROT (-1);
826 get->mt_capacity = get_ll (mp ()->Capacity);
827 get->mt_remaining = get_ll (mp ()->Remaining);
831 get->mt_gstat |= GMT_DR_OPEN (-1);
833 if (buffer_writes ())
834 get->mt_gstat |= GMT_IM_REP_EN (-1); /* TODO: Async writes */
836 if (tstat == ERROR_DEVICE_REQUIRES_CLEANING)
837 get->mt_gstat |= GMT_CLN (-1);
839 /* Cygwin specials: */
840 if (dp ()->ReportSetmarks)
841 get->mt_gstat |= GMT_REP_SM (-1);
842 if (dp ()->DataPadding)
843 get->mt_gstat |= GMT_PADDING (-1);
845 get->mt_gstat |= GMT_HW_ECC (-1);
846 if (dp ()->Compression)
847 get->mt_gstat |= GMT_HW_COMP (-1);
849 get->mt_gstat |= GMT_TWO_FM (-1);
851 get->mt_gstat |= GMT_FAST_MTEOM (-1);
853 get->mt_gstat |= GMT_AUTO_LOCK (-1);
855 get->mt_gstat |= GMT_SYSV (-1);
857 get->mt_gstat |= GMT_NOWAIT (-1);
859 get->mt_gstat |= GMT_ASYNC (-1);
861 get->mt_erreg = 0; /* FIXME: No softerr counting */
863 get->mt_minblksize = dp ()->MinimumBlockSize;
864 get->mt_maxblksize = dp ()->MaximumBlockSize;
865 get->mt_defblksize = dp ()->DefaultBlockSize;
866 get->mt_featureslow = dp ()->FeaturesLow;
867 get->mt_featureshigh = dp ()->FeaturesHigh;
868 get->mt_eotwarningzonesize = dp ()->EOTWarningZoneSize;
874 mtinfo_drive::set_options (HANDLE mt, long options)
876 long what = (options & MT_ST_OPTIONS);
877 bool call_setparams = false;
879 TAPE_SET_DRIVE_PARAMETERS sdp =
884 dp ()->ReportSetmarks,
885 dp ()->EOTWarningZoneSize
892 if (options == 0 || options == 1)
894 buffer_writes ((options == 1));
898 buffer_writes (!!(options & MT_ST_BUFFER_WRITES));
899 async_writes (!!(options & MT_ST_ASYNC_WRITES));
900 two_fm (!!(options & MT_ST_TWO_FM));
901 fast_eom (!!(options & MT_ST_FAST_MTEOM));
902 auto_lock (!!(options & MT_ST_AUTO_LOCK));
903 sysv (!!(options & MT_ST_SYSV));
904 nowait (!!(options & MT_ST_NOWAIT));
905 if (get_feature (TAPE_DRIVE_SET_ECC))
906 sdp.ECC = !!(options & MT_ST_ECC);
907 if (get_feature (TAPE_DRIVE_SET_PADDING))
908 sdp.DataPadding = !!(options & MT_ST_PADDING);
909 if (get_feature (TAPE_DRIVE_SET_REPORT_SMKS))
910 sdp.ReportSetmarks = !!(options & MT_ST_REPORT_SM);
911 if (sdp.ECC != dp ()->ECC || sdp.DataPadding != dp ()->DataPadding
912 || sdp.ReportSetmarks != dp ()->ReportSetmarks)
913 call_setparams = true;
915 case MT_ST_SETBOOLEANS:
916 case MT_ST_CLEARBOOLEANS:
917 set = (what == MT_ST_SETBOOLEANS);
918 if (options & MT_ST_BUFFER_WRITES)
920 if (options & MT_ST_ASYNC_WRITES)
922 if (options & MT_ST_TWO_FM)
924 if (options & MT_ST_FAST_MTEOM)
926 if (options & MT_ST_AUTO_LOCK)
928 if (options & MT_ST_SYSV)
930 if (options & MT_ST_NOWAIT)
932 if (options & MT_ST_ECC)
934 if (options & MT_ST_PADDING)
935 sdp.DataPadding = set;
936 if (options & MT_ST_REPORT_SM)
937 sdp.ReportSetmarks = set;
938 if (sdp.ECC != dp ()->ECC || sdp.DataPadding != dp ()->DataPadding
939 || sdp.ReportSetmarks != dp ()->ReportSetmarks)
940 call_setparams = true;
942 case MT_ST_EOT_WZ_SIZE:
943 if (get_feature (TAPE_DRIVE_SET_EOT_WZ_SIZE))
945 sdp.EOTWarningZoneSize = (options & ~MT_ST_OPTIONS);
946 if (sdp.EOTWarningZoneSize != dp ()->EOTWarningZoneSize)
947 call_setparams = true;
953 TAPE_FUNC (SetTapeParameters (mt, SET_TAPE_DRIVE_INFORMATION, &sdp));
957 dp ()->ECC = sdp.ECC;
958 dp ()->DataPadding = sdp.DataPadding;
959 dp ()->ReportSetmarks = sdp.ReportSetmarks;
965 return error ("set_options");
969 mtinfo_drive::ioctl (HANDLE mt, unsigned int cmd, void *buf)
972 if (efault.faulted ())
973 return ERROR_NOACCESS;
976 struct mtop *op = (struct mtop *) buf;
977 if (lasterr == ERROR_BUS_RESET)
979 /* If a bus reset occurs, block further access to this device
980 until the user rewinds, unloads or in any other way tries
981 to maintain a well-known tape position. */
982 if (op->mt_op != MTREW && op->mt_op != MTOFFL
983 && op->mt_op != MTRETEN && op->mt_op != MTERASE
984 && op->mt_op != MTSEEK && op->mt_op != MTEOM)
985 return ERROR_BUS_RESET;
986 /* Try to maintain last lock state after bus reset. */
987 if (lock >= auto_locked && PrepareTape (mt, TAPE_LOCK, FALSE))
989 debug_printf ("Couldn't relock drive after bus reset.");
998 set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, false);
1001 set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, false);
1004 set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count, false);
1007 set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count, false);
1010 write_marks (mt, TAPE_FILEMARKS, op->mt_count);
1013 set_pos (mt, TAPE_REWIND, 0, false);
1017 prepare (mt, TAPE_UNLOAD);
1023 if (!get_feature (TAPE_DRIVE_TENSION))
1024 lasterr = ERROR_INVALID_PARAMETER;
1025 else if (!set_pos (mt, TAPE_REWIND, 0, false))
1026 prepare (mt, TAPE_TENSION);
1029 set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, true);
1032 set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, true);
1035 if (fast_eom () && get_feature (TAPE_DRIVE_END_OF_DATA))
1036 set_pos (mt, TAPE_SPACE_END_OF_DATA, 0, false);
1038 set_pos (mt, TAPE_SPACE_FILEMARKS, 32767, false);
1041 erase (mt, TAPE_ERASE_LONG);
1046 lasterr = ERROR_INVALID_PARAMETER;
1049 if (!get_feature (TAPE_DRIVE_SET_BLOCK_SIZE))
1051 lasterr = ERROR_INVALID_PARAMETER;
1054 if ((DWORD) op->mt_count == mp ()->BlockSize)
1056 /* Nothing has changed. */
1060 if ((op->mt_count == 0 && !get_feature (TAPE_DRIVE_VARIABLE_BLOCK))
1061 || (op->mt_count > 0
1062 && ((DWORD) op->mt_count < dp ()->MinimumBlockSize
1063 || (DWORD) op->mt_count > dp ()->MaximumBlockSize)))
1065 lasterr = ERROR_INVALID_PARAMETER;
1068 if (set_blocksize (mt, op->mt_count)
1069 && lasterr == ERROR_INVALID_FUNCTION)
1070 lasterr = ERROR_INVALID_BLOCK_LENGTH;
1073 if (get_feature (TAPE_DRIVE_LOGICAL_BLK))
1074 set_pos (mt, TAPE_LOGICAL_BLOCK, op->mt_count, false);
1075 else if (!get_pos (mt))
1076 set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS,
1077 op->mt_count - block, false);
1081 op->mt_count = block;
1084 set_pos (mt, TAPE_SPACE_SETMARKS, op->mt_count, false);
1087 set_pos (mt, TAPE_SPACE_SETMARKS, -op->mt_count, false);
1090 write_marks (mt, TAPE_SETMARKS, op->mt_count);
1093 prepare (mt, TAPE_LOCK);
1096 prepare (mt, TAPE_UNLOCK);
1099 prepare (mt, TAPE_LOAD);
1102 set_compression (mt, op->mt_count);
1105 set_partition (mt, op->mt_count);
1108 create_partitions (mt, op->mt_count);
1110 case MTSETDRVBUFFER:
1111 set_options (mt, op->mt_count);
1115 lasterr = ERROR_INVALID_PARAMETER;
1119 else if (cmd == MTIOCGET)
1120 get_status (mt, (struct mtget *) buf);
1121 else if (cmd == MTIOCPOS && !get_pos (mt))
1122 ((struct mtpos *) buf)->mt_blkno = block;
1127 /**********************************************************************/
1131 mtinfo::initialize ()
1133 char name[CYG_MAX_PATH];
1136 shared_name (name, "mtinfo_mutex", 0);
1137 if (!(mtx = CreateMutex (&sec_all_nih, FALSE, name)))
1138 api_fatal ("CreateMutex '%s', %E. Terminating.", name);
1139 WaitForSingleObject (mtx, INFINITE);
1142 magic = MTINFO_MAGIC;
1143 version = MTINFO_VERSION;
1144 for (unsigned i = 0; i < MAX_DRIVE_NUM; ++i)
1145 drive (i)->initialize (i, true);
1153 if (magic != MTINFO_MAGIC)
1154 api_fatal ("MT magic number screwed up: %lu, should be %lu",
1155 magic, MTINFO_MAGIC);
1156 if (version != MTINFO_VERSION)
1157 system_printf ("MT version number mismatch: %lu, should be %lu",
1158 version, MTINFO_VERSION);
1167 shared_locations sh_mtinfo = SH_MTINFO;
1168 mt = (mtinfo *) open_shared ("mtinfo", MTINFO_VERSION, cygheap->mt_h, sizeof (mtinfo), sh_mtinfo);
1169 ProtectHandleINH (cygheap->mt_h);
1173 /**********************************************************************/
1174 /* fhandler_dev_tape */
1176 #define lock(err_ret_val) if (!_lock ()) return err_ret_val;
1179 fhandler_dev_tape::_lock ()
1181 HANDLE obj[2] = { mt_mtx, signal_arrived };
1182 BOOL ret = WaitForMultipleObjects (2, obj, FALSE, INFINITE) == WAIT_OBJECT_0;
1185 debug_printf ("signal_arrived"); \
1192 fhandler_dev_tape::unlock (int ret)
1194 ReleaseMutex (mt_mtx);
1198 fhandler_dev_tape::fhandler_dev_tape ()
1199 : fhandler_dev_raw ()
1201 debug_printf ("unit: %d", dev ().minor);
1205 fhandler_dev_tape::open (int flags, mode_t)
1209 if (driveno () >= MAX_DRIVE_NUM)
1214 if (!(mt_mtx = CreateMutex (&sec_all, TRUE, NULL)))
1219 /* The O_TEXT flag is used to indicate write-through (non buffered writes)
1220 to the underlying fhandler_dev_raw::open call. */
1222 if (!mt->drive (driveno ())->buffer_writes ())
1224 ret = fhandler_dev_raw::open (flags);
1227 mt->drive (driveno ())->open (get_handle ());
1229 /* In append mode, seek to beginning of next filemark */
1230 if (flags & O_APPEND)
1231 mt->drive (driveno ())->set_pos (get_handle (),
1232 TAPE_SPACE_FILEMARKS, 1, true);
1234 devbufsiz = mt->drive (driveno ())->dp ()->MaximumBlockSize;
1235 devbuf = new char [devbufsiz];
1236 devbufstart = devbufend = 0;
1239 ReleaseMutex (mt_mtx);
1244 fhandler_dev_tape::close ()
1252 ret = mt->drive (driveno ())->close (get_handle (), is_rewind_device ());
1254 __seterrno_from_win_error (ret);
1255 cret = fhandler_dev_raw::close ();
1259 CloseHandle (mt_evt);
1260 CloseHandle (mt_mtx);
1261 return ret ? -1 : cret;
1265 fhandler_dev_tape::raw_read (void *ptr, size_t &ulen)
1267 char *buf = (char *) ptr;
1270 size_t bytes_to_read;
1271 size_t bytes_read = 0;
1274 if (lastblk_to_read ())
1276 lastblk_to_read (false);
1285 block_size = mt->drive (driveno ())->mp ()->BlockSize;
1286 if (devbufend > devbufstart)
1288 bytes_to_read = min (len, devbufend - devbufstart);
1289 debug_printf ("read %d bytes from buffer (rest %d)",
1290 bytes_to_read, devbufend - devbufstart - bytes_to_read);
1291 memcpy (buf, devbuf + devbufstart, bytes_to_read);
1292 len -= bytes_to_read;
1293 bytes_read += bytes_to_read;
1294 buf += bytes_to_read;
1295 devbufstart += bytes_to_read;
1296 if (devbufstart == devbufend)
1297 devbufstart = devbufend = 0;
1298 /* If a switch to variable block_size occured, just return the buffer
1299 remains until the buffer is empty, then proceed with usual variable
1300 block size handling (one block per read call). */
1306 if (!mt_evt && !(mt_evt = CreateEvent (&sec_none, TRUE, FALSE, NULL)))
1307 debug_printf ("Creating event failed, %E");
1308 size_t block_fit = !block_size ? len : rounddown(len, block_size);
1311 debug_printf ("read %d bytes from tape (rest %d)",
1312 block_fit, len - block_fit);
1313 ret = mt->drive (driveno ())->read (get_handle (), mt_evt, buf,
1316 __seterrno_from_win_error (ret);
1320 bytes_read += block_fit;
1322 /* Only one block in each read call, please. */
1329 lastblk_to_read (true);
1332 if (!ret && len > 0)
1334 debug_printf ("read %d bytes from tape (one block)", block_size);
1335 ret = mt->drive (driveno ())->read (get_handle (), mt_evt, devbuf,
1338 __seterrno_from_win_error (ret);
1339 else if (block_size)
1342 devbufend = block_size;
1344 memcpy (buf, devbuf, len);
1346 else if (bytes_read)
1347 lastblk_to_read (true);
1350 ulen = (ret ? (size_t) -1 : bytes_read);
1355 fhandler_dev_tape::raw_write (const void *ptr, size_t len)
1358 if (!mt_evt && !(mt_evt = CreateEvent (&sec_none, TRUE, FALSE, NULL)))
1359 debug_printf ("Creating event failed, %E");
1360 int ret = mt->drive (driveno ())->write (get_handle (), mt_evt, ptr, len);
1362 __seterrno_from_win_error (ret);
1363 return unlock (ret ? -1 : (int) len);
1367 fhandler_dev_tape::lseek (_off64_t offset, int whence)
1372 _off64_t ret = ILLEGAL_SEEK;
1374 lock (ILLEGAL_SEEK);
1376 debug_printf ("lseek (%s, %d, %d)", get_name (), offset, whence);
1378 block_size = mt->drive (driveno ())->mp ()->BlockSize;
1379 if (block_size == 0)
1385 if (ioctl (MTIOCPOS, &pos))
1393 if (ioctl (MTIOCTOP, &op))
1397 if (whence == SEEK_SET && offset < 0)
1411 op.mt_count = offset / block_size
1412 - (whence == SEEK_SET ? pos.mt_blkno : 0);
1414 if (op.mt_count < 0)
1417 op.mt_count = -op.mt_count;
1420 if (ioctl (MTIOCTOP, &op) || ioctl (MTIOCPOS, &pos))
1423 ret = pos.mt_blkno * block_size;
1426 return unlock (ret);
1430 fhandler_dev_tape::fstat (struct __stat64 *buf)
1434 if (driveno () >= MAX_DRIVE_NUM)
1439 if (!(ret = fhandler_base::fstat (buf)))
1445 fhandler_dev_tape::dup (fhandler_base *child)
1448 fhandler_dev_tape *fh = (fhandler_dev_tape *) child;
1449 if (!DuplicateHandle (hMainProc, mt_mtx, hMainProc, &fh->mt_mtx, 0, TRUE,
1450 DUPLICATE_SAME_ACCESS))
1452 debug_printf ("dup(%s) failed, mutex handle %x, %E",
1453 get_name (), mt_mtx);
1459 !DuplicateHandle (hMainProc, mt_evt, hMainProc, &fh->mt_evt, 0, TRUE,
1460 DUPLICATE_SAME_ACCESS))
1462 debug_printf ("dup(%s) failed, event handle %x, %E",
1463 get_name (), mt_evt);
1467 return unlock (fhandler_dev_raw::dup (child));
1471 fhandler_dev_tape::fixup_after_fork (HANDLE parent)
1473 fhandler_dev_raw::fixup_after_fork (parent);
1474 fork_fixup (parent, mt_mtx, "mt_mtx");
1476 fork_fixup (parent, mt_evt, "mt_evt");
1480 fhandler_dev_tape::set_close_on_exec (bool val)
1482 fhandler_dev_raw::set_close_on_exec (val);
1483 set_no_inheritance (mt_mtx, val);
1485 set_no_inheritance (mt_evt, val);
1489 fhandler_dev_tape::ioctl (unsigned int cmd, void *buf)
1493 if (cmd == MTIOCTOP || cmd == MTIOCGET || cmd == MTIOCPOS)
1495 ret = mt->drive (driveno ())->ioctl (get_handle (), cmd, buf);
1497 __seterrno_from_win_error (ret);
1498 return unlock (ret ? -1 : 0);
1500 return unlock (fhandler_dev_raw::ioctl (cmd, buf));