forked from rbowler/spinhawk
/
w32stape.c
1249 lines (1045 loc) · 40 KB
/
w32stape.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
////////////////////////////////////////////////////////////////////////////////////
// W32STAPE.C -- Hercules Win32 SCSI Tape handling module
//
// (c) Copyright "Fish" (David B. Trout), 2005-2009. Released under
// the Q Public License (http://www.hercules-390.org/herclic.html)
// as modifications to Hercules.
////////////////////////////////////////////////////////////////////////////////////
//
// This module contains only WIN32 support for SCSI tapes.
// Primary SCSI Tape support is in module 'scsitape.c'...
//
// PROGRAMMING NOTE: we rely on the known fact that
// 'NO_ERROR' == 0 and 'INVALID_HANDLE_VALUE' == -1.
//
////////////////////////////////////////////////////////////////////////////////////
// $Id$
//
// $Log$
// Revision 1.19 2009/01/23 13:17:44 bernard
// copyright notice
//
// Revision 1.18 2008/03/31 06:36:49 fish
// (untab)
//
// Revision 1.17 2008/03/30 02:51:34 fish
// Fix SCSI tape EOV (end of volume) processing
//
// Revision 1.16 2007/11/30 14:54:33 jmaynard
// Changed conmicro.cx to hercules-390.org or conmicro.com, as needed.
//
// Revision 1.15 2007/07/24 22:36:33 fish
// Fix tape Synchronize CCW (x'43') to do actual commit
//
// Revision 1.14 2007/07/24 21:57:29 fish
// Fix Win32 SCSI tape "Locate" and "ReadBlockId" SNAFU
//
// Revision 1.13 2007/06/23 00:04:19 ivan
// Update copyright notices to include current year (2007)
//
// Revision 1.12 2006/12/08 09:43:33 jj
// Add CVS message log
//
#include "hstdinc.h"
#define _W32STAPE_C_
#define _HTAPE_DLL_
#include "hercules.h"
#include "w32stape.h"
#include "tapedev.h" // (need IS_TAPE_BLKID_BOT)
#ifdef _MSVC_
////////////////////////////////////////////////////////////////////////////////////
// Global data...
#define W32STAPE_MAX_FDNUMS (32) // (admitedly low, but easily increased)
typedef int ifd_t; // (internal fd)
typedef int ufd_t; // (user fd)
#define W32STAPE_IFD2UFD( ifd ) ((ufd_t)( (ifd) | 0x7F000000 ))
#define W32STAPE_UFD2IFD( ufd ) ((ifd_t)( (ufd) & ~0x7F000000 ))
static BYTE g_ifds [ W32STAPE_MAX_FDNUMS ] = {0}; // (0 == avail, 0xFF == used)
static HANDLE g_handles [ W32STAPE_MAX_FDNUMS ] = {0}; // (WIN32 handles)
static char* g_fnames [ W32STAPE_MAX_FDNUMS ] = {0}; // (for posterity)
static U32 g_fstats [ W32STAPE_MAX_FDNUMS ] = {0}; // (running status)
static U32 g_BOTmsk [ W32STAPE_MAX_FDNUMS ] = {0}; // (BOT block-id mask)
static U32 g_BOTbot [ W32STAPE_MAX_FDNUMS ] = {0}; // (BOT block-id value)
static TAPE_GET_DRIVE_PARAMETERS g_drive_parms [ W32STAPE_MAX_FDNUMS ] = {0}; // (drive parameters)
static LOCK g_lock; // (master global access lock)
#define lock() obtain_w32stape_lock()
#define unlock() release_lock( &g_lock )
static void obtain_w32stape_lock()
{
static int bDidInit = 0;
static int bInitBusy = 1;
if (!bDidInit)
{
bDidInit = 1;
initialize_lock ( &g_lock );
memset( g_ifds, 0x00, sizeof ( g_ifds ) );
memset( g_handles, 0x00, sizeof ( g_handles ) );
memset( g_fnames, 0x00, sizeof ( g_fnames ) );
memset( g_fstats, 0x00, sizeof ( g_fstats ) );
memset( g_BOTmsk, 0xFF, sizeof ( g_BOTmsk ) );
memset( g_BOTbot, 0x00, sizeof ( g_BOTbot ) );
bInitBusy = 0;
}
while (bInitBusy) Sleep(10);
obtain_lock ( &g_lock );
}
////////////////////////////////////////////////////////////////////////////////////
// Allocate an internal fd number...
static
ifd_t w32_alloc_ifd()
{
ifd_t ifd = -1;
errno = EMFILE;
lock();
{
BYTE* pifd_slot = memchr( g_ifds, 0, W32STAPE_MAX_FDNUMS );
if (pifd_slot)
{
int n = (int) (pifd_slot - g_ifds);
if ( n >= 0 && n < W32STAPE_MAX_FDNUMS )
{
*pifd_slot = 1;
errno = 0;
ifd = n;
}
}
}
unlock();
return ifd;
}
////////////////////////////////////////////////////////////////////////////////////
// Release an internal fd number...
static
int w32_free_ifd( ifd_t ifd )
{
int rc = 0;
errno = 0;
lock();
{
if ( ifd >= 0 && ifd < W32STAPE_MAX_FDNUMS )
g_ifds [ ifd ] = 0;
else
{
rc = -1;
errno = EBADF;
}
}
unlock();
return rc;
}
////////////////////////////////////////////////////////////////////////////////////
// Retrieve the status of the tape drive...
static
DWORD w32_get_tape_status ( HANDLE hFile )
{
// ***************************************************************
// PROGRAMMING NOTE: it is THIS LOOP (retrieving the status
// of the tape drive) that takes UP TO *10* SECONDS TO COMPLETE
// if there is no tape mounted on the drive whereas it completes
// immediately when there IS a tape mounted! I have no idea why
// Windows behave so unusually/inefficiently in this way! - Fish
// ***************************************************************
DWORD dwTapeStatus;
// (NOTE: see also: KB 111837: "ERROR_BUS_RESET May Be Benign")
do dwTapeStatus = GetTapeStatus( hFile );
while (ERROR_BUS_RESET == dwTapeStatus);
return dwTapeStatus;
}
////////////////////////////////////////////////////////////////////////////////////
// Open tape device...
DLL_EXPORT
ufd_t w32_open_tape ( const char* path, int oflag, ... )
{
ifd_t ifd;
HANDLE hFile;
char szTapeDeviceName[10];
const char* pszTapeDevNum;
DWORD dwDesiredAccess, dwSizeofDriveParms, dwRetCode;
// Reserve an fd number right away and bail if none available...
if ( (ifd = w32_alloc_ifd()) < 0 )
return -1;
// If they specified a Windows device name,
// use it as-is.
if (1
&& strnfilenamecmp( path, "\\\\.\\", 4 ) == 0
&& path [4] != 0
)
{
strlcpy( szTapeDeviceName, path, sizeof(szTapeDeviceName) );
}
else // (not a Windows device name)
{
// The device name is a Cygwin/*nix device name.
// Name must be either "/dev/nst0" or "/dev/st0"
if (1
&& strnfilenamecmp( path, "/dev/", 5 ) == 0
&& (
strnfilenamecmp( (pszTapeDevNum=path+8)-3, "nst", 3 ) == 0
||
strnfilenamecmp( (pszTapeDevNum=path+7)-2, "st", 2 ) == 0
)
&& strlen(pszTapeDevNum) == 1
&& isdigit(*pszTapeDevNum)
)
{
// Change it to a Windows device name (e.g. \\.\Tape0)
strlcpy( szTapeDeviceName, WIN32_TAPE_DEVICE_NAME, sizeof(szTapeDeviceName) );
szTapeDeviceName[8] = *pszTapeDevNum;
szTapeDeviceName[9] = 0;
// PROGRAMMING NOTE: the "rewind at close" option (implied by
// virtue of the filename being "/dev/st0" and not "/dev/nst0")
// was handled (detected/remembered) by the higher-level caller.
}
else
{
VERIFY( w32_free_ifd( ifd ) == 0 );
errno = EINVAL; // (bad device name)
return -1; // (open failure)
}
}
// We only support O_BINARY with either O_RDWR or O_RDONLY
if (1
&& (( O_BINARY | O_RDWR ) != oflag)
&& (( O_BINARY | O_RDONLY) != oflag)
)
{
VERIFY( w32_free_ifd( ifd ) == 0 );
errno = EINVAL; // (invalid open flags)
return -1; // (open failure)
}
// Set desired access
dwDesiredAccess = GENERIC_READ;
if ( oflag & O_RDWR )
dwDesiredAccess |= GENERIC_WRITE;
// Open the tape drive...
hFile = CreateFile
(
szTapeDeviceName, // filename
dwDesiredAccess, // desired access
0, // share mode (0 == exclusive)
NULL, // security == default
OPEN_EXISTING, // "file" (device actually) must already exist
0, // no special access flags needed
NULL // not using template
);
if ( INVALID_HANDLE_VALUE == hFile )
{
int save_errno = w32_trans_w32error( GetLastError() );
VERIFY( w32_free_ifd( ifd ) == 0 );
errno = save_errno;
return -1;
}
// Save drive parameters for later...
memset( &g_drive_parms[ifd], 0, sizeof(TAPE_GET_DRIVE_PARAMETERS) );
dwSizeofDriveParms = sizeof(TAPE_GET_DRIVE_PARAMETERS);
do
{
dwRetCode = GetTapeParameters
(
hFile,
GET_TAPE_DRIVE_INFORMATION,
&dwSizeofDriveParms,
&g_drive_parms[ifd]
);
}
while ((NO_ERROR != dwRetCode) // (if not normal completion,
&& // check for retry conditions)
(0
|| ERROR_MEDIA_CHANGED == dwRetCode // (likely but unimportant; retry)
|| ERROR_BUS_RESET == dwRetCode // (unlikely but possible; retry)
));
// Did that work?
if (NO_ERROR != dwRetCode)
{
int save_errno = w32_trans_w32error( GetLastError() );
CloseHandle( hFile );
VERIFY( w32_free_ifd( ifd ) == 0 );
errno = save_errno;
return -1;
}
ASSERT( NO_ERROR == dwRetCode );
ASSERT( sizeof(TAPE_GET_DRIVE_PARAMETERS) == dwSizeofDriveParms );
// Save control info & return their file descriptor...
g_handles [ ifd ] = hFile; // (WIN32 handle)
g_fnames [ ifd ] = strdup( path ); // (for posterity)
g_fstats [ ifd ] = GMT_ONLINE (0xFFFFFFFF); // (initial status)
g_BOTmsk [ ifd ] = 0xFFFFFFFF; // (BOT block-id mask)
g_BOTbot [ ifd ] = 0x00000000; // (BOT block-id value)
return W32STAPE_IFD2UFD( ifd ); // (user fd result)
}
////////////////////////////////////////////////////////////////////////////////////
// Define physical BOT block-id mask / value...
// PROGRAMMING NOTE: For the time being, we require 'tapedev.c' to provide to us
// the information we need in order to detect physical BOT (load-point). This is
// only until such time as I can add SCSI PassThru support to Hercules so that we
// can talk SCSI directly to the device ourselves (to determine such things as
// what type of device (manufacturer/model) we're dealing with, etc).
DLL_EXPORT
int w32_define_BOT ( ufd_t ufd, U32 msk, U32 bot )
{
ifd_t ifd = W32STAPE_UFD2IFD( ufd );
lock();
if (0
|| ifd < 0
|| ifd >= W32STAPE_MAX_FDNUMS
|| g_ifds[ ifd ] == 0
)
{
unlock();
errno = EBADF;
return -1;
}
g_BOTmsk [ ifd ] = msk; // (BOT block-id mask)
g_BOTbot [ ifd ] = bot; // (BOT block-id value)
unlock();
return 0;
}
////////////////////////////////////////////////////////////////////////////////////
// Post-process a tape i/o return code...
//
// Examine 'errno' (which should have been manually set to the return
// code from the current i/o) and update the internal status appropriately,
// depending on what type of error it was (tapemark, etc)...
//
// -------------------------------------------------------------
// *** THIS FUNCTION SHOULD BE CALLED AFTER EVERY TAPE I/O ***
// -------------------------------------------------------------
//
// An errno of 'EINTR' means the error was spurious (media changed, etc)
// and that the caller should try the same i/o again (retry their i/o).
//
// EXAMPLE:
//
// do
// {
// errno = SetTapePosition( ... );
// errno = w32_internal_rc ( pStat );
// }
// while ( EINTR == errno );
// return errno ? -1 : 0;
//
////////////////////////////////////////////////////////////////////////////////////
// *** THIS FUNCTION SHOULD BE CALLED AFTER EVERY TAPE I/O ***
static
int w32_internal_rc ( U32* pStat )
{
ASSERT( pStat ); // (sanity check)
// PROGRAMMING NOTE: the 'door open' (no tape in drive) and the
// 'write protected' statuses are "sticky" in that they never change
// until a new/different tape is mounted. All the other statuses
// however, change dynamically as one does i/o to the tape...
if (0
|| ERROR_BUS_RESET == errno // (See KB 111837: "ERROR_BUS_RESET May Be Benign")
|| ERROR_MEDIA_CHANGED == errno
|| ERROR_DEVICE_NOT_CONNECTED == errno // (shouldn't occur but we'll check anyway)
|| ERROR_DEV_NOT_EXIST == errno // (shouldn't occur but we'll check anyway)
|| ERROR_FILE_NOT_FOUND == errno // (shouldn't occur but we'll check anyway)
)
{
*pStat &= ~GMT_DR_OPEN (0xFFFFFFFF);
*pStat &= ~GMT_WR_PROT (0xFFFFFFFF);
}
// (see PROGRAMMING NOTE above)
*pStat &= ~GMT_BOT (0xFFFFFFFF);
*pStat &= ~GMT_SM (0xFFFFFFFF);
*pStat &= ~GMT_EOF (0xFFFFFFFF);
*pStat &= ~GMT_EOT (0xFFFFFFFF);
*pStat &= ~GMT_EOD (0xFFFFFFFF);
if (0
|| ERROR_BUS_RESET == errno // (spurious error; retry)
|| ERROR_MEDIA_CHANGED == errno // (spurious error; retry)
// || ERROR_DEVICE_NOT_CONNECTED == errno // (PERM ERROR! NO RETRY!)
// || ERROR_DEV_NOT_EXIST == errno // (PERM ERROR! NO RETRY!)
// || ERROR_FILE_NOT_FOUND == errno // (PERM ERROR! NO RETRY!)
)
{
return EINTR; // (Interrupted system call; Retry)
}
// (see PROGRAMMING NOTE further above)
switch (errno)
{
default: break; // (leave errno set to whatever it already is)
case NO_ERROR: errno = 0; break; // (normal expected i/o result)
case ERROR_BEGINNING_OF_MEDIA: *pStat |= GMT_BOT (0xFFFFFFFF); errno = EIO; break;
case ERROR_END_OF_MEDIA: *pStat |= GMT_EOT (0xFFFFFFFF); errno = ENOSPC; break;
// "ERROR_END_OF_MEDIA"
//
// Msg: "The physical end of the tape has been reached."
//
// The EOT warning reflector has been reached or passed (i.e. you're
// now/still in the "EOT Warning Zone" area). Writing additional data
// and/or tapemarks may still be possible depending on the size of the
// EOT Warning Zone (as set by a SetTapeParameters call with a non-zero
// EOTWarningZoneSize value (if supported; see further below)) and
// how much data you've already written to the EOT Warning Zone area
// (i.e. once you're in the warning area, this "error" occurs after
// EACH and EVERY I/O [in the warning zone area] until the ABSOLUTE
// physical end-of-tape (ERROR_EOM_OVERFLOW) is reached; see below).
//
//
// ***********************
// ** IMPORTANT NOTE! **
// ***********************
//
// This is NOT actually an "error"!!!
//
//
// When this "error" occurs, your "ReadFile" and/or "WriteFile" call
// returns 'FALSE' even though ALL of your requested data was actually
// written successfully!! This can be verified by checking to ensure
// the returned "number of bytes written" actually matches the amount
// you asked to be written. If they're the same (and they ALWAYS will
// be for this specific "error" code), then it means this "error" is
// NOT actually an error at all, but rather just a WARNING instead!!
// (Had it been an actual i/o error, the error code would have been
// some other DIFFERENT error code value instead!!)
//
//
// ***********************
// ** ALSO IMPORTANT! **
// ***********************
// See also:
//
// http://fixunix.com/storage/205622-bug-dlttape-sys-no-eot-warning.html
//
// for ADDITIONAL IMPORTANT INFORMATION regarding always having to
// specifically request that this "error" code be returned to you:
//
// Even when a drive reports it does not support the setting of the
// the 'EOTWarningZoneSize' value (i.e. the FeaturesLow field of the
// GetTapeParameters call returns '0' for TAPE_DRIVE_SET_EOT_WZ_SIZE
// field), it may still be possible for "ERROR_END_OF_MEDIA" warnings
// to be generated anyway by simply calling SetTapeParameters with a
// non-zero 'EOTWarningZoneSize' value anyway.
//
// The reason for this is because some drives may not allow CHANGING
// the value (thus the reason for it reporting that setting the value
// is not supported), but may nevertheless still support the ENABLING
// of their own hard-coded internal value. That is to say, while the
// size of the warning zone may not be modifiable (as it may be hard-
// coded and thus unchangeable), the drive may still have the ability
// to REPORT reaching the EOT Warning zone IF SPECIFICALLY REQUESTED
// TO DO SO! (which is presumably what requesting a non-zero Warning
// Zone size would end up doing: i.e. even though such calls APPEAR
// to fail, they actually DO succeed in accomplishing SOMETHING, just
// not what you originally/specifically requested).
//
// Thus calling SetTapeParameters with a non-zero 'EOTWarningZoneSize'
// value might very well succeed anyway even though GetTapeParameters
// reports that doing so is not supported, and by so doing, may cause
// the drive to begin reporting of "ERROR_END_OF_MEDIA" (whereas not
// attempting to do so would end up leaving the drive in its default
// non-reporting mode. That is to say, you should ALWAYS try setting
// a non-zero 'EOTWarningZoneSize' value, ignoring any "unsupported"
// error code that may be returned from such a call.)
case ERROR_EOM_OVERFLOW: *pStat |= GMT_EOT (0xFFFFFFFF); errno = EIO; break;
// "ERROR_EOM_OVERFLOW"
//
// Msg: "Physical end of tape encountered."
//
// This error code means that the actual physical end-of-media has been
// reached, and no more data can be written to the tape. This includes
// tapemarks as well.
//
// ***********************
// ** IMPORTANT NOTE! **
// ***********************
//
// This is a HARD (UNRECOVERABLE) error!!
//
// To be programmatically informed of when you are coming close to the
// physical end-of-the-tape (such that you could be assured room still
// remained to write logical end-of-volume labels for example), simply
// call SetTapeParameters with a non-zero 'EOTWarningZoneSize' value
// and treat any "ERROR_END_OF_MEDIA" "errors" received when writing
// as warnings instead. (See prior discussion of "ERROR_END_OF_MEDIA"
// return code further above)
case ERROR_NO_DATA_DETECTED: *pStat |= GMT_EOD (0xFFFFFFFF); errno = EIO; break;
case ERROR_FILEMARK_DETECTED: *pStat |= GMT_EOF (0xFFFFFFFF); errno = EIO; break;
case ERROR_SETMARK_DETECTED: *pStat |= GMT_SM (0xFFFFFFFF); errno = EIO; break;
case ERROR_NOT_READY: *pStat |= GMT_DR_OPEN (0xFFFFFFFF); errno = ENOMEDIUM; break;
case ERROR_NO_MEDIA_IN_DRIVE: *pStat |= GMT_DR_OPEN (0xFFFFFFFF); errno = ENOMEDIUM; break;
case ERROR_WRITE_PROTECT: *pStat |= GMT_WR_PROT (0xFFFFFFFF); errno = EROFS; break;
}
return errno;
}
////////////////////////////////////////////////////////////////////////////////////
// (forward references for private helper functions)
int w32_internal_mtop ( HANDLE hFile, U32* pStat, struct mtop* mtop, ifd_t ifd );
int w32_internal_mtget ( HANDLE hFile, U32* pStat, struct mtget* mtget, ifd_t ifd );
int w32_internal_mtpos ( HANDLE hFile, U32* pStat, DWORD* pdwLogPos,
DWORD* pdwAbsPos, ifd_t ifd );
////////////////////////////////////////////////////////////////////////////////////
// Close tape device...
DLL_EXPORT
int w32_close_tape ( ufd_t ufd )
{
ifd_t ifd = W32STAPE_UFD2IFD( ufd );
int rc = -1;
errno = EBADF;
lock();
if (1
&& ifd >= 0
&& ifd < W32STAPE_MAX_FDNUMS
&& g_ifds[ ifd ] != 0
)
{
// Deallocate resources
HANDLE hFile = g_handles[ ifd ];
char* pName = g_fnames [ ifd ];
g_handles[ ifd ] = NULL;
g_fnames [ ifd ] = NULL;
g_fstats [ ifd ] = GMT_DR_OPEN (0xFFFFFFFF);
g_BOTmsk [ ifd ] = 0xFFFFFFFF;
g_BOTbot [ ifd ] = 0x00000000;
VERIFY( w32_free_ifd( ifd ) == 0 );
// Close the file...
free( pName );
errno = CloseHandle( hFile ) ? 0 : w32_trans_w32error( GetLastError() );
rc = errno ? -1 : 0;
}
unlock();
return rc;
}
////////////////////////////////////////////////////////////////////////////////////
// Read tape...
DLL_EXPORT
ssize_t w32_read_tape ( ufd_t ufd, void* buf, size_t nbyte )
{
BOOL bSuccess;
DWORD dwBytesRead;
DWORD dwLastError;
ifd_t ifd = W32STAPE_UFD2IFD( ufd );
U32* pStat = NULL;
HANDLE hFile;
if (!buf)
{
errno = EINVAL;
return -1;
}
lock();
if (0
|| ifd < 0
|| ifd >= W32STAPE_MAX_FDNUMS
|| g_ifds[ ifd ] == 0
)
{
unlock();
errno = EBADF;
return -1;
}
unlock();
hFile = g_handles[ ifd ];
pStat = &g_fstats[ ifd ];
// Do the i/o, save results, update device status
// (based on the results), then check results...
do
{
dwBytesRead = 0;
bSuccess = ReadFile( hFile, buf, (DWORD)nbyte, &dwBytesRead, NULL );
errno = (dwLastError = GetLastError());
errno = w32_internal_rc ( pStat );
}
while ( !bSuccess && EINTR == errno );
// Success? (see: "ERROR_END_OF_MEDIA" in function 'w32_internal_rc')
if (bSuccess || ERROR_END_OF_MEDIA == dwLastError)
{
ASSERT( bSuccess || ENOSPC == errno );
return ( (ssize_t) dwBytesRead );
}
ASSERT( !bSuccess && ERROR_END_OF_MEDIA != dwLastError && ENOSPC != errno );
// The i/o "failed". Check to see if it was just a tapemark...
if ( EIO == errno && GMT_EOF( *pStat ) )
{
ASSERT( ERROR_FILEMARK_DETECTED == dwLastError );
return 0; // (tapemark)
}
// EIO != errno || !GMT_EOF( *pStat ) --> bona fide i/o error...
ASSERT( ERROR_FILEMARK_DETECTED != dwLastError );
return -1;
}
////////////////////////////////////////////////////////////////////////////////////
// Write tape...
DLL_EXPORT
ssize_t w32_write_tape ( ufd_t ufd, const void* buf, size_t nbyte )
{
BOOL bSuccess;
DWORD dwBytesWritten;
DWORD dwLastError;
ifd_t ifd = W32STAPE_UFD2IFD( ufd );
U32* pStat = NULL;
HANDLE hFile;
if (!buf)
{
errno = EINVAL;
return -1;
}
lock();
if (0
|| ifd < 0
|| ifd >= W32STAPE_MAX_FDNUMS
|| g_ifds[ ifd ] == 0
)
{
unlock();
errno = EBADF;
return -1;
}
unlock();
hFile = g_handles[ ifd ];
pStat = &g_fstats[ ifd ];
// Do the i/o, save results, update device status
// (based on the results), then check results...
do
{
dwBytesWritten = 0;
bSuccess = WriteFile( hFile, buf, (DWORD)nbyte, &dwBytesWritten, NULL );
errno = (dwLastError = GetLastError());
errno = w32_internal_rc ( pStat );
}
while ( !bSuccess && EINTR == errno );
// Success? (see: "ERROR_END_OF_MEDIA" in function 'w32_internal_rc')
if (bSuccess || ERROR_END_OF_MEDIA == dwLastError)
{
ASSERT( bSuccess || ENOSPC == errno );
ASSERT( ((size_t)dwBytesWritten) == nbyte ); // (MUST be true!!)
return ( (ssize_t) dwBytesWritten );
}
// I/O error...
ASSERT( !bSuccess && ERROR_END_OF_MEDIA != dwLastError && ENOSPC != errno );
return -1;
}
////////////////////////////////////////////////////////////////////////////////////
// ioctl... (perform some type of control function, e.g. fsf, rewind, etc)
DLL_EXPORT
int w32_ioctl_tape ( ufd_t ufd, int request, ... )
{
va_list vl;
void* ptr = NULL;
int rc = 0;
ifd_t ifd = W32STAPE_UFD2IFD( ufd );
U32* pStat = NULL;
HANDLE hFile;
lock();
if (0
|| ifd < 0
|| ifd >= W32STAPE_MAX_FDNUMS
|| g_ifds[ ifd ] == 0
)
{
unlock();
errno = EBADF;
return -1;
}
unlock();
hFile = g_handles[ ifd ];
pStat = &g_fstats[ ifd ];
va_start ( vl, request );
ptr = va_arg( vl, void* );
if ( !ptr )
{
errno = EINVAL;
return -1;
}
switch (request)
{
case MTIOCTOP: // (perform tape operation)
{
struct mtop* mtop = ptr;
rc = w32_internal_mtop ( hFile, pStat, mtop, ifd );
}
break;
case MTIOCGET: // (retrieve tape status)
{
struct mtget* mtget = ptr;
memset( mtget, 0, sizeof(*mtget) );
rc = w32_internal_mtget ( hFile, pStat, mtget, ifd );
}
break;
case MTIOCPOS: // (retrieve tape position)
{
struct mtpos* mtpos = ptr;
memset( mtpos, 0, sizeof(*mtpos) );
rc = w32_internal_mtpos( hFile, pStat, &mtpos->mt_blkno, NULL, ifd );
}
break;
default: // (invalid/unsupported ioctl code)
{
errno = EINVAL;
rc = -1;
}
break;
}
return rc;
}
////////////////////////////////////////////////////////////////////////////////////
// Private internal helper function... return 0 == success, -1 == failure
static
int w32_internal_mtop ( HANDLE hFile, U32* pStat, struct mtop* mtop, ifd_t ifd )
{
int rc = 0;
ASSERT( pStat && mtop ); // (sanity check)
// General technique: do the i/o, save results, update the
// device status (based on the results), then check results...
switch ( mtop->mt_op )
{
case MTLOAD: // (load media)
{
if ( 1 != mtop->mt_count )
{
errno = EINVAL;
rc = -1;
}
else
{
do
{
errno = PrepareTape( hFile, TAPE_LOAD, FALSE );
errno = w32_internal_rc ( pStat );
}
while ( EINTR == errno );
}
}
break;
case MTUNLOAD: // (unload media)
case MTOFFL: // (make media offline (same as unload))
{
if ( 1 != mtop->mt_count )
{
errno = EINVAL;
rc = -1;
}
else
{
do
{
errno = PrepareTape( hFile, TAPE_UNLOAD, FALSE );
errno = w32_internal_rc ( pStat );
}
while ( EINTR == errno );
}
}
break;
case MTSEEK: // (position media)
{
do
{
errno = SetTapePosition( hFile, TAPE_LOGICAL_BLOCK, 0, mtop->mt_count, 0, FALSE );
errno = w32_internal_rc ( pStat );
}
while ( EINTR == errno );
}
break;
case MTREW: // (rewind)
{
if ( 1 != mtop->mt_count )
{
errno = EINVAL;
rc = -1;
}
else
{
do
{
errno = SetTapePosition( hFile, TAPE_REWIND, 0, 0, 0, FALSE );
errno = w32_internal_rc ( pStat );
}
while ( EINTR == errno );
}
}
break;
case MTFSF: // (FORWARD space FILE)
case MTBSF: // (BACKWARD space FILE)
{
if ( !mtop->mt_count )
{
errno = EINVAL;
rc = -1;
}
else
{
LARGE_INTEGER liCount;
liCount.QuadPart = mtop->mt_count;
if ( MTBSF == mtop->mt_op )
liCount.QuadPart = -liCount.QuadPart; // (negative == backwards)
do
{
errno = SetTapePosition( hFile, TAPE_SPACE_FILEMARKS, 0, liCount.LowPart, liCount.HighPart, FALSE );
errno = w32_internal_rc ( pStat );
}
while ( EINTR == errno );
}
}
break;
case MTFSR: // (FORWARD space BLOCK)
case MTBSR: // (BACKWARD space BLOCK)
{
if ( !mtop->mt_count )
{
errno = EINVAL;
rc = -1;
}
else
{
LARGE_INTEGER liCount;
liCount.QuadPart = mtop->mt_count;
if ( MTBSR == mtop->mt_op )
liCount.QuadPart = -liCount.QuadPart; // (negative == backwards)
do
{
errno = SetTapePosition( hFile, TAPE_SPACE_RELATIVE_BLOCKS, 0, liCount.LowPart, liCount.HighPart, FALSE );
errno = w32_internal_rc ( pStat );
}
while ( EINTR == errno );
}
}
break;
case MTSETBLK: // (set blocksize)
{
TAPE_SET_MEDIA_PARAMETERS media_parms;
media_parms.BlockSize = mtop->mt_count;
do
{
errno = SetTapeParameters( hFile, SET_TAPE_MEDIA_INFORMATION, &media_parms );
errno = w32_internal_rc ( pStat );
}
while ( EINTR == errno );
}
break;
case MTEOTWARN: // (set EOT Warning Zone size in bytes)
{
TAPE_SET_DRIVE_PARAMETERS set_drive_parms;
set_drive_parms.ECC = g_drive_parms[ifd].ECC;
set_drive_parms.Compression = g_drive_parms[ifd].Compression;
set_drive_parms.DataPadding = g_drive_parms[ifd].DataPadding;
set_drive_parms.ReportSetmarks = g_drive_parms[ifd].ReportSetmarks;
set_drive_parms.EOTWarningZoneSize = mtop->mt_count;
do
{
errno = SetTapeParameters( hFile, SET_TAPE_DRIVE_INFORMATION, &set_drive_parms );
errno = w32_internal_rc ( pStat );
}
while ( EINTR == errno );
}
break;
case MTWEOF: // (write TAPEMARK)
{
if ( mtop->mt_count < 0 )
{
errno = EINVAL;
rc = -1;
}
else
{
// PROGRAMMING NOTE: We prefer "long" filemarks over any other type
// because, according to the SDK documentaion:
//
// "A short filemark contains a short erase gap that cannot be
// overwritten unless the write operation is performed from the
// beginning of the partition or from an earlier long filemark."
//
// "A long filemark contains a long erase gap that allows an